home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 4287 / 4287.xpi / chrome / splitbrowser.jar / content / splitbrowser / splitbrowser.js < prev    next >
Text File  |  2009-11-05  |  124KB  |  4,165 lines

  1. var SplitBrowser = { 
  2.     initialized : false,
  3.     useSessionStore : true,
  4.     get canSave()
  5.     {
  6.         return this.initialized &&
  7.             (!this.PrivateBrowsing || !this.PrivateBrowsing.privateBrowsingEnabled);
  8.     },
  9.     
  10.     get scrollbarSize() { 
  11.         return this.getPref('splitbrowser.appearance.scrollbar.size');
  12.     },
  13.  
  14.     get subBrowserToolbarShowDelay() { 
  15.         return this.getPref('splitbrowser.delay.subbrowser.toolbar.show');
  16.     },
  17.     get subBrowserToolbarHideDelay() {
  18.         return this.getPref('splitbrowser.delay.subbrowser.toolbar.hide');
  19.     },
  20.  
  21.     get subBrowserAutoFocusDelay() { 
  22.         return this.getPref('splitbrowser.subbrowser.autoFocus') ? this.getPref('splitbrowser.delay.subbrowser.autoFocus') : -1 ;
  23.     },
  24.  
  25.     get shouldMoveSplitTab() { 
  26.         return this.getPref('splitbrowser.tab.closetab');
  27.     },
  28.  
  29.     get shouldSave() { 
  30.         return this.getPref('splitbrowser.state.restore');
  31.     },
  32.  
  33.     get isLinux() 
  34.     {
  35.         return (navigator.platform.toLowerCase().indexOf('linux') > -1);
  36.     },
  37.  
  38.     get isMac() 
  39.     {
  40.         return (navigator.platform.toLowerCase().indexOf('mac') > -1);
  41.     },
  42.     
  43.     isAccelKeyPressed : function(aEvent) 
  44.     {
  45.         return this.isMac ? aEvent.metaKey : aEvent.ctrlKey ;
  46.     },
  47.   
  48.     get tabbedBrowsingEnabled() 
  49.     {
  50.         // tabbed browsing mode is not compatible with TMP
  51.         return !(
  52.             'TM_init' in window
  53.         );
  54.     },
  55.  
  56.     get mainBrowserBox() 
  57.     {
  58.         return document.getElementById('appcontent').contentWrapper;
  59.     },
  60.  
  61.     POSITION_LEFT   : 1, 
  62.     POSITION_RIGHT  : 2,
  63.     POSITION_TOP    : 4,
  64.     POSITION_BOTTOM : 8,
  65.     POSITION_TAB    : 16,
  66.     POSITION_INSIDE : 0,
  67.  
  68.     POSITION_HORIZONTAL : 3,
  69.     POSITION_VERTICAL  : 12,
  70.  
  71.     POSITION_BEFORE : 5,
  72.     POSITION_AFTER  : 10,
  73.  
  74.     _browsers : [], 
  75.     get browsers() {
  76.         return this._browsers;
  77.     },
  78.     splitters : {},
  79.  
  80.     NSResolver : { 
  81.         lookupNamespaceURI : function(aPrefix)
  82.         {
  83.             switch (aPrefix)
  84.             {
  85.                 case 'xul':
  86.                     return 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
  87.                 case 'html':
  88.                 case 'xhtml':
  89.                     return 'http://www.w3.org/1999/xhtml';
  90.                 case 'xlink':
  91.                     return 'http://www.w3.org/1999/xlink';
  92.                 default:
  93.                     return '';
  94.             }
  95.         }
  96.     },
  97.  
  98.     ObserverService : Components 
  99.         .classes['@mozilla.org/observer-service;1']
  100.         .getService(Components.interfaces.nsIObserverService),
  101.  
  102.     PrivateBrowsing : ( 
  103.         'nsIPrivateBrowsingService' in Components.interfaces ?
  104.             Components
  105.                 .classes['@mozilla.org/privatebrowsing;1']
  106.                 .getService(Components.interfaces.nsIPrivateBrowsingService) :
  107.             null
  108.     ),
  109.  
  110.     evalInSandbox : function(aCode, aOwner) 
  111.     {
  112.         try {
  113.             var sandbox = new Components.utils.Sandbox(aOwner || 'about:blank');
  114.             return Components.utils.evalInSandbox(aCode, sandbox);
  115.         }
  116.         catch(e) {
  117.         }
  118.         return void(0);
  119.     },
  120.  
  121. /* utilities */ 
  122.     
  123.     makeURIFromSpec : function(aURI) 
  124.     {
  125.         try {
  126.             var newURI;
  127.             aURI = aURI || '';
  128.             if (aURI && aURI.indexOf('file:') == 0) {
  129.                 var fileHandler = this.mIOService.getProtocolHandler('file')
  130.                         .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
  131.                 var tempLocalFile = fileHandler.getFileFromURLSpec(aURI);
  132.                 newURI = this.mIOService.newFileURI(tempLocalFile); // we can use this instance with the nsIFileURL interface.
  133.             }
  134.             else {
  135.                 newURI = this.mIOService.newURI(aURI, null, null);
  136.             }
  137.             return newURI;
  138.         }
  139.         catch(e){
  140.         }
  141.         return null;
  142.     },
  143.     mIOService : Components
  144.             .classes['@mozilla.org/network/io-service;1']
  145.             .getService(Components.interfaces.nsIIOService),
  146.  
  147.     getCurrentDragSession : function() 
  148.     {
  149.         return Components
  150.             .classes['@mozilla.org/widget/dragservice;1']
  151.             .getService(Components.interfaces.nsIDragService)
  152.             .getCurrentSession();
  153.     },
  154.  
  155.     getSubBrowserByName : function(aName) 
  156.     {
  157.         if (aName == '_top')
  158.             return this.mainBrowserBox;
  159.         else if (!aName)
  160.             return null;
  161.  
  162.         for (var i in this._browsers)
  163.         {
  164.             if (this._browsers[i].name == aName)
  165.                 return this._browsers[i];
  166.         }
  167.  
  168.         return null;
  169.     },
  170.  
  171.     getSubBrowserById : function(aID) 
  172.     {
  173.         var windows = this.browserWindows;
  174.         var node;
  175.         for (var i = 0, maxi = windows.length; i < maxi; i++)
  176.         {
  177.             node = windows[i].document.getElementById(aID);
  178.             if (node)
  179.                 return node;
  180.         }
  181.         return null;
  182.     },
  183.     
  184.     get browserWindows() 
  185.     {
  186.         var browserWindows = [];
  187.  
  188.         var targets = this.WindowManager.getEnumerator('navigator:browser'),
  189.             target;
  190.         while (targets.hasMoreElements())
  191.         {
  192.             target = targets.getNext().QueryInterface(Components.interfaces.nsIDOMWindowInternal);
  193.             browserWindows.push(target);
  194.         }
  195.  
  196.         return browserWindows;
  197.     },
  198.  
  199.     get WindowManager() 
  200.     {
  201.         if (!this._WindowManager) {
  202.             this._WindowManager = Components
  203.                         .classes['@mozilla.org/appshell/window-mediator;1']
  204.                         .getService(Components.interfaces.nsIWindowMediator);
  205.         }
  206.         return this._WindowManager;
  207.     },
  208.     _WindowManager : null,
  209.   
  210.     getSubBrowserFromFrame : function(aFrame) 
  211.     {
  212.         var docShell = (aFrame.top || aFrame)
  213.             .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  214.             .getInterface(Components.interfaces.nsIWebNavigation)
  215.             .QueryInterface(Components.interfaces.nsIDocShell);
  216.         for (var i = 0, maxi = this._browsers.length; i < maxi; i++)
  217.         {
  218.             if (this._browsers[i].browser.docShell == docShell)
  219.                 return this._browsers[i];
  220.         }
  221.         return null;
  222.     },
  223.  
  224.     getTabFromFrame : function(aFrame) 
  225.     {
  226.         var b = this.getSubBrowserFromFrame(aFrame);
  227.         if (b)
  228.             b = b.browser
  229.         if (!b || b.localName != 'tabbrowser')
  230.             b = gBrowser;
  231.  
  232.         var tabs = this.getTabs(b);
  233.  
  234.         var docShell = (aFrame.top || aFrame)
  235.             .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  236.             .getInterface(Components.interfaces.nsIWebNavigation)
  237.             .QueryInterface(Components.interfaces.nsIDocShell);
  238.         for (var i = 0, maxi = tabs.snapshotLength; i < maxi; i++)
  239.         {
  240.             if (tabs.snapshotItem(i).linkedBrowser.docShell == docShell)
  241.                 return tabs.snapshotItem(i);
  242.         }
  243.         return null;
  244.     },
  245.  
  246.     getTabFromBrowser : function(aBrowser) 
  247.     {
  248.         var b = this.getTabBrowserFromChild(aBrowser);
  249.         var tabs = this.getTabs(b);
  250.         for (var i = 0, maxi = tabs.snapshotLength; i < maxi; i++)
  251.         {
  252.             if (tabs.snapshotItem(i).linkedBrowser == aBrowser)
  253.                 return tabs.snapshotItem(i);
  254.         }
  255.         return null;
  256.     },
  257.  
  258.     getTabBrowserFromChild : function(aNode) 
  259.     {
  260.         if (!aNode) return null;
  261.         return aNode.ownerDocument.evaluate(
  262.                 'ancestor-or-self::*[local-name()="tabbrowser"]',
  263.                 aNode,
  264.                 null,
  265.                 XPathResult.FIRST_ORDERED_NODE_TYPE,
  266.                 null
  267.             ).singleNodeValue;
  268.     },
  269.  
  270.     getTabFromChild : function(aNode) 
  271.     {
  272.         if (!aNode) return null;
  273.         return aNode.ownerDocument.evaluate(
  274.                 'ancestor-or-self::*[local-name()="tab" and ancestor::*[local-name()="tabbrowser"]]',
  275.                 aNode,
  276.                 null,
  277.                 XPathResult.FIRST_ORDERED_NODE_TYPE,
  278.                 null
  279.             ).singleNodeValue;
  280.     },
  281.  
  282.     getTabs : function(aTabBrowser) 
  283.     {
  284.         return aTabBrowser.ownerDocument.evaluate(
  285.                 'descendant::*[local-name()="tab"]',
  286.                 aTabBrowser.mTabContainer,
  287.                 null,
  288.                 XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
  289.                 null
  290.             );
  291.     },
  292.  
  293.     getTabsArray : function(aTabBrowser) 
  294.     {
  295.         var tabs = this.getTabs(aTabBrowser);
  296.         var array = [];
  297.         for (var i = 0, maxi = tabs.snapshotLength; i < maxi; i++)
  298.         {
  299.             array.push(tabs.snapshotItem(i));
  300.         }
  301.         return array;
  302.     },
  303.  
  304.     getFirstTab : function(aTabBrowser) 
  305.     {
  306.         return aTabBrowser.ownerDocument.evaluate(
  307.                 'descendant::*[local-name()="tab"][1]',
  308.                 aTabBrowser.mTabContainer,
  309.                 null,
  310.                 XPathResult.FIRST_ORDERED_NODE_TYPE,
  311.                 null
  312.             ).singleNodeValue;
  313.     },
  314.  
  315.     getLastTab : function(aTabBrowser) 
  316.     {
  317.         return aTabBrowser.ownerDocument.evaluate(
  318.                 'descendant::*[local-name()="tab"][last()]',
  319.                 aTabBrowser.mTabContainer,
  320.                 null,
  321.                 XPathResult.FIRST_ORDERED_NODE_TYPE,
  322.                 null
  323.             ).singleNodeValue;
  324.     },
  325.  
  326.     getSubBrowserFromChild : function(aNode) 
  327.     {
  328.         if (!aNode) return null;
  329.         return aNode.ownerDocument.evaluate(
  330.                 'ancestor-or-self::*[local-name()="subbrowser"]',
  331.                 aNode,
  332.                 null,
  333.                 XPathResult.FIRST_ORDERED_NODE_TYPE,
  334.                 null
  335.             ).singleNodeValue;
  336.     },
  337.  
  338.     getSubBrowserAndBrowserFromFrame : function(aFrame) 
  339.     {
  340.         var docShell = aFrame.top
  341.             .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  342.             .getInterface(Components.interfaces.nsIWebNavigation)
  343.             .QueryInterface(Components.interfaces.nsIDocShell);
  344.  
  345.         for (var i = 0, maxi = this._browsers.length; i < maxi; i++)
  346.         {
  347.             if (this._browsers[i].browser.localName == 'tabbrowser') {
  348.                 for (var j = 0, maxj = this._browsers[i].browser.browsers.length; j < maxj; j++)
  349.                 {
  350.                     if (this._browsers[i].browser.browsers[j].docShell == docShell)
  351.                         return {
  352.                             subBrowser : this._browsers[i],
  353.                             browser    : this._browsers[i].browser.browsers[j]
  354.                         };
  355.                 }
  356.             }
  357.             else if (this._browsers[i].browser.docShell == docShell) {
  358.                 return {
  359.                     subBrowser : this._browsers[i],
  360.                     browser    : this._browsers[i].browser
  361.                 };
  362.             }
  363.         }
  364.  
  365.         for (var j = 0, maxj = gBrowser.browsers.length; j < maxj; j++)
  366.         {
  367.             if (gBrowser.browsers[j].docShell == docShell)
  368.                 return {
  369.                     subBrowser : null,
  370.                     browser    : gBrowser.browsers[j]
  371.                 };
  372.         }
  373.  
  374.         return {
  375.             subBrowser : null,
  376.             browser    : null
  377.         };
  378.     },
  379.  
  380.     get activeSubBrowser() 
  381.     {
  382.         return this._mFocusedSubBrowser;
  383.     },
  384.     set activeSubBrowser(val)
  385.     {
  386.         var old = this.activeSubBrowser;
  387.         try {
  388.             if (old && old != val && old.focused) old.focused = false;
  389.         }
  390.         catch(e) {
  391.         }
  392.         this._mFocusedSubBrowser = val || this.mainBrowserBox;
  393.         this.activeSubBrowser.focused = true;
  394.  
  395.  
  396.         var newEvent = document.createEvent('Events');
  397.         newEvent.initEvent('SubBrowserFocusMoved', false, true);
  398.         newEvent.lastFocused = old;
  399.         document.documentElement.dispatchEvent(newEvent);
  400.  
  401.         return val;
  402.     },
  403.     _mFocusedSubBrowser : null,
  404.     
  405.     get activeBrowser() 
  406.     {
  407.         var b = this.activeSubBrowser;
  408.         return b && b.browser ? b.browser : gBrowser ;
  409.     },
  410.   
  411.     updateStatus : function() 
  412.     {
  413.         if (this.updateStatusTimer) {
  414.             window.clearTimeout(this.updateStatusTimer);
  415.             this.updateStatusTimer = null;
  416.         }
  417.         this.updateStatusTimer = window.setTimeout(function(aSelf) {
  418.             aSelf.updateStatusCallback();
  419.         }, 1, this);
  420.     },
  421.     
  422.     updateStatusCallback : function() 
  423.     {
  424.         if (!this._browsers.length) {
  425.             document.documentElement.removeAttribute('splitbrowser-split');
  426.             this.featuresForSplitBrowsersBroadcaster.setAttribute('disabled', true);
  427.             this.collapseAllBroadcaster.setAttribute('disabled', true);
  428.             this.expandAllBroadcaster.setAttribute('disabled', true);
  429.         }
  430.         else {
  431.             document.documentElement.setAttribute('splitbrowser-split', true);
  432.             this.featuresForSplitBrowsersBroadcaster.removeAttribute('disabled');
  433.  
  434.             var collapsed = 0;
  435.             var expanded  = 0;
  436.             for (var i = 0, maxi = this._browsers.length; i < maxi; i++)
  437.             {
  438.                 if (this._browsers[i].contentCollapsed)
  439.                     collapsed++;
  440.                 else
  441.                     expanded++;
  442.  
  443.                 if (collapsed && expanded) break;
  444.             }
  445.             if (collapsed)
  446.                 this.expandAllBroadcaster.removeAttribute('disabled');
  447.             else
  448.                 this.expandAllBroadcaster.setAttribute('disabled', true);
  449.  
  450.             if (expanded)
  451.                 this.collapseAllBroadcaster.removeAttribute('disabled');
  452.             else
  453.                 this.collapseAllBroadcaster.setAttribute('disabled', true);
  454.         }
  455.  
  456.         if (this.useSessionStore && this.canSave) this.save();
  457.         this.updateStatusTimer = null;
  458.     },
  459.  
  460.     get featuresForSplitBrowsersBroadcaster() 
  461.     {
  462.         return document.getElementById('splitbrowser-featuresForSplitBrowsers-broadcaster');
  463.     },
  464.  
  465.     get collapseAllBroadcaster() 
  466.     {
  467.         return document.getElementById('splitbrowser-collapseAll-broadcaster');
  468.     },
  469.  
  470.     get expandAllBroadcaster() 
  471.     {
  472.         return document.getElementById('splitbrowser-expandAll-broadcaster');
  473.     },
  474.  
  475.     get featuresForMultipleTabsBroadcaster() 
  476.     {
  477.         return document.getElementById('splitbrowser-featuresForMultipleTabs-broadcaster');
  478.     },
  479.  
  480.     get undoBroadcaster() 
  481.     {
  482.         return document.getElementById('splitbrowser-undo-broadcaster');
  483.     },
  484.   
  485.     updateMenu : function(aPopup) 
  486.     {
  487.         this.updateMultipleTabsState();
  488.  
  489.         var syncScroll = document.getElementById('splitbrowser-syncScroll-broadcaster');
  490.         if (this.mainBrowserBox.syncScroll)
  491.             syncScroll.setAttribute('checked', true);
  492.         else
  493.             syncScroll.removeAttribute('checked');
  494.     },
  495.  
  496.     updateMultipleTabsState : function() 
  497.     {
  498.         var tabBroadcaster = this.featuresForMultipleTabsBroadcaster;
  499.         var b = SplitBrowser.activeBrowser;
  500.         if (b.localName != 'tabbrowser') b = gBrowser;
  501.         if (this.getTabs(b).snapshotLength > 1)
  502.             tabBroadcaster.removeAttribute('disabled');
  503.         else
  504.             tabBroadcaster.setAttribute('disabled', true);
  505.     },
  506.  
  507.     fireSubBrowserAddRequestEventFromFrame : function(aFrame, aBrowser, aPosition, aEventTarget, aCopy) 
  508.     {
  509.         var tab = this.getTabFromFrame(aFrame);
  510.         var uri = aFrame.top.location.href;
  511.         var subBrowser = aBrowser || this.getSubBrowserFromFrame(aFrame) || this.mainBrowserBox;
  512.         var browser = subBrowser.browser;
  513.         if (browser.localName == 'tabbrowser')
  514.             browser = browser.getBrowserForTab(browser.selectedTab);
  515.  
  516.         var newEvent = document.createEvent('Events');
  517.         newEvent.initEvent('SubBrowserAddRequest', false, true);
  518.  
  519.         var appcontent = document.getElementById('appcontent');
  520.  
  521.         newEvent.targetSubBrowser = subBrowser;
  522.         newEvent.targetContainer  = subBrowser.parentContainer || appcontent;
  523.         newEvent.targetPosition   = aPosition;
  524.         newEvent.targetURI        = null;
  525.         newEvent.sourceBrowser    = browser;
  526.         newEvent.sourceTab        = tab;
  527.         newEvent.isCopy           = aCopy;
  528.         (aEventTarget || appcontent).dispatchEvent(newEvent);
  529.     },
  530.  
  531.     fireSubBrowserAddRequestEvent : function(aURI, aBrowser, aPosition, aEventTarget) 
  532.     {
  533.         var newEvent = document.createEvent('Events');
  534.         newEvent.initEvent('SubBrowserAddRequest', false, true);
  535.  
  536.         var appcontent = document.getElementById('appcontent');
  537.  
  538.         if (!aBrowser)
  539.             aBrowser = this.mainBrowserBox;
  540.  
  541.         newEvent.targetSubBrowser = aBrowser;
  542.         newEvent.targetContainer  = aBrowser.parentContainer || appcontent;
  543.         newEvent.targetPosition   = aPosition;
  544.         newEvent.targetURI        = aURI;
  545.         (aEventTarget || appcontent).dispatchEvent(newEvent);
  546.     },
  547.  
  548.     fieldNormalClicks : function(aName) 
  549.     {
  550.         return /^(search)$/.test(aName);
  551.     },
  552.  
  553.     isEventFromKeyboardShortcut : function(aEvent) 
  554.     {
  555.         if (!aEvent) return false;
  556.         if (aEvent.type == 'command') aEvent = aEvent.sourceEvent;
  557.         if (!aEvent) return false;
  558.         return (aEvent.type.indexOf('key') == 0 || aEvent.originalTarget.localName == 'key');
  559.     },
  560.  
  561.     isEventFiredOnTabbar : function(aEvent, aTabBrowser) 
  562.     {
  563.         if (!aTabBrowser || aTabBrowser.localName != 'tabbrowser') return false;
  564.         var box = aTabBrowser.mStrip.boxObject;
  565.         return (
  566.             box.screenX <= aEvent.screenX &&
  567.             box.screenX + box.width >= aEvent.screenX &&
  568.             box.screenY <= aEvent.screenY &&
  569.             box.screenY + box.height >= aEvent.screenY
  570.             );
  571.     },
  572.   
  573. /* add sub-browser (split contents) */ 
  574.     
  575.     addSubBrowser : function(aURI, aBrowser, aPosition, aName) 
  576.     {
  577.         fullScreenCanvas.show();
  578.  
  579.         if (!aURI) aURI = 'about:blank';
  580.         if (!aPosition) aPosition = this.POSITION_BOTTOM;
  581.  
  582.         var appcontent = document.getElementById('appcontent');
  583.         var b = aBrowser || this.getSubBrowserFromFrame(document.commandDispatcher.focusedWindow.top);
  584.         var target = (b && b.parentContainer) ? b.parentContainer : appcontent ;
  585.         var hContainer = target.hContainer;
  586.         var vContainer = target.vContainer;
  587.  
  588.         var box = b ? b.boxObject : null ;
  589.         if (!box) box = gBrowser.boxObject;
  590.  
  591.         var width  = (aPosition & this.POSITION_HORIZONTAL) ? parseInt(box.width / 5 * 2) : -1 ;
  592.         var height = (aPosition & this.POSITION_VERTICAL) ? parseInt(box.height / 5 * 2) : -1 ;
  593.  
  594.         var refNode = (aPosition & this.POSITION_HORIZONTAL) ? (b || this.mainBrowserBox ) : hContainer ;
  595.  
  596.         var data = null;
  597.         if (aURI && aURI.indexOf('subbrowser\n') == 0) {
  598.             try {
  599.                 data = this.evalInSandbox('('+aURI.replace('subbrowser\n', '')+')');
  600.             }
  601.             catch(e) {
  602.             }
  603.         }
  604.  
  605.         var sourceSubBrowser = null;
  606.         if (data) {
  607.             sourceSubBrowser = SplitBrowser.getSubBrowserById(data.id);
  608.             if (!sourceSubBrowser) {
  609.                 aURI = data.uri;
  610.             }
  611.             else {
  612.                 aURI   = null;
  613.                 if (aPosition & this.POSITION_HORIZONTAL && sourceSubBrowser.parentOrient == 'horizontal')
  614.                     width = data.width;
  615.                 if (aPosition & this.POSITION_VERTICAL && sourceSubBrowser.parentOrient == 'vertical')
  616.                     height = data.height;
  617.             }
  618.         }
  619.  
  620.         var browser = this.createSubBrowser(aURI);
  621.         if (aName) browser.setAttribute('name', aName);
  622.  
  623.         var container = this.addContainerTo(target, aPosition, refNode, width, height, browser);
  624.  
  625.         if (sourceSubBrowser) {
  626.             browser.syncScroll = sourceSubBrowser.syncScroll;
  627.             browser.name = sourceSubBrowser.name + (data.clone ? '-clone'+parseInt(Math.random() * 65000) : '' );
  628.             window.setTimeout(
  629.                 (data.clone ? this.cloneBrowser : this.swapBrowser ),
  630.                 0,
  631.                 sourceSubBrowser.browser,
  632.                 browser.browser,
  633.                 (data.clone ?
  634.                     null :
  635.                     function() {
  636.                         var fromRemote = sourceSubBrowser.ownerDocument != document;
  637.                         sourceSubBrowser.close(true);
  638.                         if (fromRemote) fullScreenCanvas.hide();
  639.                     }
  640.                 )
  641.             );
  642.             if (data.clone) fullScreenCanvas.hide();
  643.         }
  644.         else {
  645.             fullScreenCanvas.hide();
  646.         }
  647.  
  648.         return browser;
  649.     },
  650.     
  651.     addSubBrowserFromTab : function(aTab, aPosition, aPositionTarget, aCopy) 
  652.     {
  653.         fullScreenCanvas.show();
  654.         var b = this.getTabBrowserFromChild(aTab);
  655.         if (aTab.localName != 'tab')
  656.             aTab = b.selectedTab;
  657.  
  658.         var uri = this.tabbedBrowsingEnabled ? null : aTab.linkedBrowser.currentURI.spec ;
  659.  
  660.         var browser = this.addSubBrowser(uri, (aPositionTarget || b.parentSubBrowser || this.mainBrowserBox), aPosition);
  661.  
  662.         window.setTimeout(
  663.             (aCopy ? this.cloneBrowser : this.swapBrowser ),
  664.             0,
  665.             aTab.linkedBrowser,
  666.             browser.browser,
  667.             (aCopy ? null : function() { if (aTab.parentNode) b.removeTab(aTab); fullScreenCanvas.hide(); } )
  668.         );
  669.         if (aCopy) fullScreenCanvas.hide();
  670.  
  671.         return browser;
  672.     },
  673.     
  674.     cloneBrowser : function(aSource, aTarget, aCallback) 
  675.     {
  676.         var state = SplitBrowser.serializeBrowserState(aSource);
  677.         SplitBrowser.deserializeBrowserState(aTarget, state, aCallback);
  678.     },
  679.  
  680.     swapBrowser : function(aSource, aTarget, aCallback) 
  681.     {
  682.         if (aTarget.localName == 'tabbrowser' && aSource.localName == 'browser')
  683.             aTarget = aTarget.selectedTab.linkedBrowser;
  684.  
  685.         if (
  686.             aSource.localName != aTarget.localName ||
  687.             !('swapDocShells' in (aSource.localName == 'tabbrowser' ? aSource.selectedTab.linkedBrowser : aSource)) ||
  688.             !SplitBrowser.getTabBrowserFromChild(aTarget)
  689.             ) {
  690.             SplitBrowser.cloneBrowser(aSource, aTarget, aCallback);
  691.             return;
  692.         }
  693.         if (aSource.localName == 'tabbrowser' &&
  694.             aTarget.localName == 'tabbrowser') {
  695.             let sourceCount = SplitBrowser.getTabs(aSource).snapshotLength;
  696.             let targetCount = SplitBrowser.getTabs(aTarget).snapshotLength;
  697.             while (sourceCount > targetCount)
  698.             {
  699.                 aTarget.addTab();
  700.                 targetCount++;
  701.             }
  702.             let sourceTabs = SplitBrowser.getTabsArray(aSource);
  703.  
  704.             let targetTabs = SplitBrowser.getTabs(aTarget);
  705.             let count = targetTabs.snapshotLength;
  706.             let removedTabs = [];
  707.             for (let i = 0, maxi = count - sourceTabs.length; i < maxi; i++)
  708.             {
  709.                 removeTabs.push(targetTabs.snapshotItem(count-1-i));
  710.             }
  711.             removedTabs.forEach(function(aTab) {
  712.                 aTarget.removeTab(aTab);
  713.             });
  714.  
  715.             targetTabs = SplitBrowser.getTabsArray(aTarget);
  716.             sourceTabs.forEach(function(aSourceTab, aIndex) {
  717.                 SplitBrowser.swapOneBrowser(
  718.                     aSourceTab.linkedBrowser,
  719.                     targetTabs[aIndex].linkedBrowser
  720.                 );
  721.             }, this);
  722.         }
  723.         else {
  724.             SplitBrowser.swapOneBrowser(aSource, aTarget);
  725.         }
  726.         if (aCallback) aCallback();
  727.     },
  728.     swapOneBrowser : function(aSource, aTarget)
  729.     {
  730.         var sourceTab = this.getTabFromBrowser(aSource);
  731.         var targetTab = this.getTabFromBrowser(aTarget);
  732.         var sourceTabBrowser = this.getTabBrowserFromChild(sourceTab);
  733.         var targetTabBrowser = this.getTabBrowserFromChild(targetTab);
  734.  
  735.         if (
  736.             this.getTabs(sourceTabBrowser).snapshotLength == 1 &&
  737.             (
  738.                 (
  739.                     sourceTabBrowser.parentSubBrowser &&
  740.                     sourceTabBrowser.parentSubBrowser.updateToolbarForCurrentTab
  741.                 ) ||
  742.                 sourceTabBrowser.ownerDocument.defaultView.SplitBrowser.browsers.length
  743.             )
  744.             ) {
  745.             sourceTabBrowser.addTab();
  746.         }
  747.  
  748.         targetTab.linkedBrowser.stop();
  749.         targetTab.linkedBrowser.docShell;
  750.         targetTabBrowser.swapBrowsersAndCloseOther(targetTab, sourceTab);
  751.         targetTabBrowser.setTabTitle(targetTab);
  752.  
  753.         if (targetTab.selected &&
  754.             targetTabBrowser.parentSubBrowser &&
  755.             targetTabBrowser.parentSubBrowser.updateToolbarForCurrentTab)
  756.             targetTabBrowser.parentSubBrowser.updateToolbarForCurrentTab(true);
  757.     },
  758.    
  759.     addContainerTo : function(aParent, aPosition, aRefNode, aWidth, aHeight, aContent) 
  760.     {
  761.         if (aPosition & this.POSITION_HORIZONTAL)
  762.             aHeight = -1;
  763.         else
  764.             aWidth = -1;
  765.  
  766.         var container = this.createContainer(aWidth, aHeight);
  767.         var hContainer = aParent.hContainer;
  768.         var vContainer = aParent.vContainer;
  769.  
  770.         var splitter = this.createSplitter(aPosition);
  771.         container.setAttribute('splitter', ((aPosition & this.POSITION_AFTER) ? 'before' : 'after' ));
  772.  
  773.         switch (aPosition)
  774.         {
  775.             case this.POSITION_LEFT:
  776.                 if (!aRefNode || aRefNode.parentNode != hContainer)
  777.                     aRefNode = hContainer.firstChild;
  778.                 if (aContent) {
  779.                     aRefNode.setAttribute('width', aRefNode.boxObject.width - aWidth);
  780.                     aContent.setAttribute('width', aWidth);
  781.                 }
  782.                 hContainer.insertBefore(container, aRefNode);
  783.                 hContainer.insertBefore(splitter, aRefNode);
  784.                 break;
  785.  
  786.             default:
  787.             case this.POSITION_RIGHT:
  788.                 if (!aRefNode || aRefNode.parentNode != hContainer)
  789.                     aRefNode = hContainer.lastChild;
  790.                 if (aContent) {
  791.                     aRefNode.setAttribute('width', aRefNode.boxObject.width - aWidth);
  792.                     aContent.setAttribute('width', aWidth);
  793.                 }
  794.                 aRefNode = aRefNode.nextSibling;
  795.                 hContainer.insertBefore(splitter, aRefNode);
  796.                 hContainer.insertBefore(container, aRefNode);
  797.                 break;
  798.  
  799.             case this.POSITION_TOP:
  800.                 if (!aRefNode || aRefNode.parentNode != vContainer)
  801.                     aRefNode = vContainer.firstChild;
  802.                 if (aContent) {
  803.                     aRefNode.setAttribute('height', aRefNode.boxObject.height - aHeight);
  804.                     aContent.setAttribute('height', aHeight);
  805.                 }
  806.                 vContainer.insertBefore(container, aRefNode);
  807.                 vContainer.insertBefore(splitter, aRefNode);
  808.                 break;
  809.  
  810.             case this.POSITION_BOTTOM:
  811.                 if (!aRefNode || aRefNode.parentNode != vContainer)
  812.                     aRefNode = vContainer.lastChild;
  813.                 if (aContent) {
  814.                     aRefNode.setAttribute('height', aRefNode.boxObject.height - aHeight);
  815.                     aContent.setAttribute('height', aHeight);
  816.                 }
  817.                 aRefNode = aRefNode.nextSibling;
  818.                 vContainer.insertBefore(splitter, aRefNode);
  819.                 vContainer.insertBefore(container, aRefNode);
  820.                 break;
  821.         }
  822.  
  823.         if (aContent)
  824.             container.hContainer.appendChild(aContent);
  825.  
  826.         return container;
  827.     },
  828.  
  829.     createSubBrowser : function(aURI) 
  830.     {
  831.         var browser = document.createElement('subbrowser');
  832.         browser.setAttribute('flex', 1);
  833.         if (aURI && aURI != 'about:blank')
  834.             browser.setAttribute('src', aURI);
  835.  
  836.         browser.setAttribute('browsertype', this.tabbedBrowsingEnabled ? 'tabbrowser' : 'simple' );
  837.         browser.setAttribute('id', 'splitbrowser-subbrowser-'+parseInt(Math.random() * 65000));
  838.  
  839.         this._browsers.push(browser);
  840.  
  841.         return browser;
  842.     },
  843.  
  844.     createContainer : function(aWidth, aHeight) 
  845.     {
  846.         var container = document.createElement('subbrowser-container');
  847.         container.setAttribute('flex', 1);
  848.         if (aWidth > -1) container.width = aWidth;
  849.         if (aHeight > -1) container.height = aHeight;
  850.  
  851.         return container;
  852.     },
  853.  
  854.     createSplitter : function(aPosition) 
  855.     {
  856.         var splitter = document.createElement('splitter');
  857. //        splitter.setAttribute('contextmenu', 'subbrowser-splitter-contextmenu');
  858.         splitter.setAttribute('class', 'subbrowser-splitter');
  859.         splitter.setAttribute('state', 'open');
  860.         splitter.setAttribute('orient', ((aPosition & this.POSITION_HORIZONTAL) ? 'horizontal' : 'vertical' ));
  861.  
  862.         splitter.setAttribute('_collapse', ((aPosition & this.POSITION_AFTER) ? 'after' : 'before' ));
  863.         if (!this.getPref('splitbrowser.show.toolbar.always'))
  864.             splitter.setAttribute('collapse', splitter.getAttribute('_collapse'));
  865.  
  866.         var prop = (aPosition & this.POSITION_HORIZONTAL) ? 'width' : 'height' ;
  867.         splitter.setAttribute('onmousedown', 'SplitBrowser.updateSplitterSideBoxes(event, "'+prop+'");');
  868.         splitter.setAttribute('onmouseup', 'SplitBrowser.updateSplitterSideBoxes(event, "'+prop+'");');
  869.  
  870. //        splitter.appendChild(document.createElement('grippy'));
  871.  
  872.         var id = 'splitbrowser-splitter-'+parseInt(Math.random() * 65000);
  873.         splitter.setAttribute('id', id);
  874.         this.splitters[id] = splitter;
  875.  
  876.         return splitter;
  877.     },
  878.   
  879. /* remove sub-browser (unsplit) */ 
  880.     
  881.     removeSubBrowser : function(aBrowser, aPreventRestore) 
  882.     {
  883.         fullScreenCanvas.show();
  884.  
  885.         var c = aBrowser.flexibleParent;
  886.         if (c &&
  887.             (c.nextSibling ? c.nextSibling.nextSibling : c.previousSibling.previousSibling).contentCollapsed) {
  888.             var splitter = c.nextSibling || c.previousSibling;
  889.             var orient  = (splitter.getAttribute('orient') != 'vertical' ? 'horizontal' : 'vertical' );
  890.             aBrowser.clearMaxSizeProp(c.nextSibling ? c.nextSibling.nextSibling : c.previousSibling.previousSibling, orient == 'horizontal' ? 'width' : 'height' );
  891.         }
  892.  
  893.  
  894. //dump('SubBrowserRemoveRequest\n');
  895.         var appcontent = document.getElementById('appcontent');
  896.         var browser   = aBrowser;
  897.         var container = browser.parentContainer || appcontent;
  898.  
  899.         for (var i = 0, maxi = this._browsers.length; i < maxi; i++)
  900.         {
  901.             if (this._browsers[i] == browser) {
  902.                 this._browsers.splice(i, 1);
  903.                 break;
  904.             }
  905.         }
  906.  
  907.         browser.destroy(aPreventRestore);
  908.         browser.parentNode.removeChild(browser);
  909.  
  910.         this.cleanUpContainer(container);
  911.  
  912.         fullScreenCanvas.hide();
  913.     },
  914.     
  915.     cleanUpContainer : function(aContainer) 
  916.     {
  917.         var container = aContainer;
  918.         var parentContainer = container.parentContainer;
  919.  
  920.         var cont = container.hContainer;
  921.         if (cont) {
  922.             if (!cont.hasChildNodes()) {
  923.                 var box;
  924.                 if (cont.previousSibling &&
  925.                     cont.previousSibling.localName == 'splitter') {
  926.                     box = cont.previousSibling.previousSibling;
  927.                     if (box) {
  928.                         box.height = box.boxObject.height + cont.boxObject.height;
  929.                         box.removeAttribute('collapsed');
  930.                     }
  931.                     this.removeSplitter(cont.previousSibling);
  932.                 }
  933.                 else if (cont.nextSibling &&
  934.                     cont.nextSibling.localName == 'splitter') {
  935.                     box = cont.nextSibling.nextSibling;
  936.                     if (box) {
  937.                         box.height = box.boxObject.height + cont.boxObject.height;
  938.                         box.removeAttribute('collapsed');
  939.                     }
  940.                     this.removeSplitter(cont.nextSibling);
  941.                 }
  942.                 container.vContainer.removeChild(cont);
  943.                 container.hContainer = null;
  944.             }
  945.             else {
  946.                 this.cleanUpSplitters(cont);
  947.             }
  948.         }
  949.  
  950.         var cont = container.vContainer;
  951.         if (!cont.hasChildNodes()) {
  952.             var box;
  953.             if (container.previousSibling && container.previousSibling.localName == 'splitter') {
  954.                 box = container.previousSibling.previousSibling;
  955.                 if (box) {
  956.                     box.width = box.boxObject.width + container.boxObject.width;
  957.                     box.removeAttribute('collapsed');
  958.                 }
  959.                 this.removeSplitter(container.previousSibling);
  960.             }
  961.             else if (container.nextSibling && container.nextSibling.localName == 'splitter') {
  962.                 box = container.nextSibling.nextSibling;
  963.                 if (box) {
  964.                     box.width = box.boxObject.width + container.boxObject.width;
  965.                     box.removeAttribute('collapsed');
  966.                 }
  967.                 this.removeSplitter(container.nextSibling);
  968.             }
  969.             container.parentNode.removeChild(container);
  970.         }
  971.         else {
  972.             this.cleanUpSplitters(cont);
  973.         }
  974.  
  975.         if (parentContainer) {
  976.             this.cleanUpContainer(parentContainer);
  977.         }
  978.     },
  979.     cleanUpSplitters : function(aContainer)
  980.     {
  981.         if (aContainer.childNodes.length % 2 != 0) return;
  982.  
  983.         if (aContainer.firstChild.localName == 'splitter') {
  984.             this.removeSplitter(aContainer.firstChild);
  985.         }
  986.         else if (aContainer.lastChild.localName == 'splitter') {
  987.             this.removeSplitter(aContainer.lastChild);
  988.         }
  989.         else {
  990.             for (var i = 0, maxi = aContainer.childNodes.length-1; i < maxi; i++)
  991.             {
  992.                 if (aContainer.childNodes[i].localName == 'splitter' &&
  993.                     aContainer.childNodes[i+1].localName == 'splitter') {
  994.                     this.removeSplitter(aContainer.childNodes[i]);
  995.                     break;
  996.                 }
  997.             }
  998.         }
  999.     },
  1000.     removeSplitter : function(aSplitter)
  1001.     {
  1002.         delete this.splitters[aSplitter.getAttribute('id')];
  1003.         aSplitter.parentNode.removeChild(aSplitter);
  1004.     },
  1005.   
  1006.     removeAllSubBrowsers : function(aPreventRestore) 
  1007.     {
  1008.         for (var i = this._browsers.length-1; i > -1; i--)
  1009.         {
  1010.             this.removeSubBrowser(this._browsers[i], aPreventRestore);
  1011.         }
  1012.     },
  1013.   
  1014. /* features */ 
  1015.     
  1016.     collapseAllSubBrowsers : function() 
  1017.     {
  1018.         this._browsers.forEach(function(aBrowser) {
  1019.             if (!aBrowser.contentCollapsed)
  1020.                 aBrowser.collapse();
  1021.         });
  1022.     },
  1023.  
  1024.     expandAllSubBrowsers : function() 
  1025.     {
  1026.         this._browsers.forEach(function(aBrowser) {
  1027.             if (aBrowser.contentCollapsed)
  1028.                 aBrowser.expand(true);
  1029.         });
  1030.     },
  1031.  
  1032.     layoutTabs : function(aSubBrowser, aStyle) 
  1033.     {
  1034.         var b    = aSubBrowser.browser;
  1035.         var tabs = this.getTabsArray(b);
  1036.  
  1037.         var isAfter      = false;
  1038.         var isHorizontal = (aStyle == this.LAYOUT_ON_X_AXIS);
  1039.  
  1040.         var self = this;
  1041.  
  1042.         var shouldDoFiltering = ('MultipleTabService' in window) ? MultipleTabService.hasSelection(b) : false ;
  1043.  
  1044.         var horizontalMax   = (aStyle == this.LAYOUT_GRID) ? Math.ceil(Math.sqrt(tabs.length)) : -1 ;
  1045.         var horizontalCount = 0;
  1046.  
  1047.         if (shouldDoFiltering && horizontalMax > 0) {
  1048.             horizontalMax = Math.ceil(Math.sqrt(
  1049.                 tabs.filter(function(aTab) { return MultipleTabService.isSelected(aTab); }).length
  1050.             ));
  1051.         }
  1052.  
  1053.         var vPosTarget      = null;
  1054.         var lastSubBrowser  = null;
  1055.  
  1056.         tabs.forEach(function(aTab) {
  1057.             /*
  1058.                 Æ╩ÅφüAî╗ì▌é╠â^âué═ò╜û╩é╔ôWèJé╡é╚éóüB
  1059.                 é╜é╛é╡üAâtâBâïâ^âèâôâOé≡ìséñüiæIæ≡é│éΩé╜â^âué╛é»é≡Åêù¥é╖éΘüjÅΩìçé═üAî╗ì▌é╠â^âuéαôWèJé╖éΘüB
  1060.             */
  1061.             var shouldSplit = shouldDoFiltering ? MultipleTabService.isSelected(aTab) : true ;
  1062.  
  1063.             if (aTab == b.selectedTab) {
  1064.                 isAfter = true;
  1065.                 horizontalCount++;
  1066.                 if (!shouldDoFiltering) return;
  1067.             }
  1068.             if (!shouldSplit) return;
  1069.  
  1070.             var pos;
  1071.             /*
  1072.                 ìíé╠â^âuéµéΦîπé╠â^âué≡ôWèJé╖éΘì█é╔é═üAì┼îπé╔ôWèJé╡é╜ò¬èäâuâëâEâUé≡èεÅÇé╔é╖éΘüB
  1073.                 é╗éñé┼é╚éóé╞üAì┼îπé╔ôWèJé╡é╜ò¬èäâuâëâEâUé╞î╗ì▌é╠âuâëâEâUé╠è╘é╔ôWèJé│éΩé─é╡é▄éñüB
  1074.                 é╜é╛é╡üAé▒éΩé╔é═ùßèOé¬éáéΘüBë║ïL<üª1>é≡ÄQÅ╞üB
  1075.             */
  1076.             var hPosTarget = isAfter ? lastSubBrowser : null ;
  1077.  
  1078.             if (horizontalMax > 0) { // ò╜û╩é╔Ä⌐ô«é┼ò└é╫éΘÅΩìç
  1079.                 pos = (horizontalCount < horizontalMax) ?
  1080.                     (isAfter ? self.POSITION_RIGHT : self.POSITION_LEFT ) :
  1081.                     self.POSITION_BOTTOM;
  1082.                 if (horizontalCount >= horizontalMax) {
  1083.                     horizontalCount = 1;
  1084.                     /*
  1085.                         <üª1>ìíé╠â^âuéµéΦîπé┼éαüAìsé¬ò╧éφéΘÄ₧üiăé╠â^âué≡ìíé╠âuâëâEâUé╠
  1086.                         É^ë║é╔ôWèJé╡é╚éóé╞éóé»é╚éóÄ₧üjé═özÆué╠èεÅÇé≡âèâZâbâgé╖éΘüB
  1087.                     */
  1088.                     hPosTarget      = null;
  1089.                 }
  1090.                 else
  1091.                     horizontalCount++;
  1092.             }
  1093.             else { // Éàò╜é▄é╜é═ÉéÆ╝é╔ò└é╫éΘÅΩìç
  1094.                 pos = isAfter ?
  1095.                     (isHorizontal ? self.POSITION_RIGHT : self.POSITION_BOTTOM) :
  1096.                     (isHorizontal ? self.POSITION_LEFT : self.POSITION_TOP);
  1097.             }
  1098.  
  1099.             var subbrowser = self.addSubBrowserFromTab(aTab, pos, (hPosTarget || vPosTarget), false);
  1100.             lastSubBrowser = subbrowser;
  1101.  
  1102.             if (horizontalMax > 0 && pos == self.POSITION_BOTTOM) {
  1103.                 /*
  1104.                     ìsé¬ò╧éφé┴é╜é╠é┼üAăé╠â^âué≡Éàò╜é╔ôWèJé╖éΘì█é╔é═üA
  1105.                     é▒é╠ÉVé╡éóìsé╠ò¬èäâuâëâEâUé≡èεÅÇé╔é╖éΘüB
  1106.                 */
  1107.                 vPosTarget = lastSubBrowser;
  1108.             }
  1109.         });
  1110.     },
  1111.     LAYOUT_GRID      : 0,
  1112.     LAYOUT_ON_X_AXIS : 1,
  1113.     LAYOUT_ON_Y_AXIS : 2,
  1114.  
  1115.     gatherSubBrowsers : function() 
  1116.     {
  1117.         fullScreenCanvas.show();
  1118.         this._browsers.forEach(function(aSubBrowser) {
  1119.             this.addTabsFromSubBrowserInto(aSubBrowser, gBrowser, true);
  1120.             window.setTimeout(function() {
  1121.                 aSubBrowser.close(true);
  1122.                 fullScreenCanvas.hide();
  1123.             }, 0);
  1124.         }, this);
  1125.     },
  1126.     
  1127.     addTabsFromSubBrowserInto : function(aSubBrowser, aTabBrowser, aMove) 
  1128.     {
  1129.         var newTabs = [];
  1130.         var b = aSubBrowser.browser;
  1131.         if (this.tabbedBrowsingEnabled && 'TabbrowserService' in window && aTabBrowser.tabGroupsAvailable) {
  1132.             var tabs = this.getTabsArray(b);
  1133.  
  1134.             var t = aTabBrowser.addTab();
  1135.             if (aMove)
  1136.                 this.swapBrowser(tabs[0].linkedBrowser, t.linkedBrowser);
  1137.             else
  1138.                 this.cloneBrowser(tabs[0].linkedBrowser, t.linkedBrowser);
  1139.             newTabs.push(t);
  1140.  
  1141.             tabs.forEach(function(aTab) {
  1142.                 if (aTab == tabs[0]) return;
  1143.                 var childT = aTabBrowser.addTab();
  1144.                 if (aMove)
  1145.                     this.swapBrowser(aTab.linkedBrowser, childT.linkedBrowser);
  1146.                 else
  1147.                     this.cloneBrowser(aTab.linkedBrowser, childT.linkedBrowser);
  1148.                 childT.parentTab = t;
  1149.                 aTabBrowser.moveTabToGroupEdge(childT, t);
  1150.             }, this);
  1151.         }
  1152.         else {
  1153.             var browsers = b.localName == 'tabbrowser' ? Array.slice(b.browsers) : [b] ;
  1154.             browsers.forEach(function(aBrowser) {
  1155.                 var t = aTabBrowser.addTab();
  1156.                 newTabs.push(t);
  1157.                 if (aMove)
  1158.                     this.swapBrowser(aBrowser, t.linkedBrowser);
  1159.                 else
  1160.                     this.cloneBrowser(aBrowser, t.linkedBrowser);
  1161.             }, this);
  1162.         }
  1163.         return newTabs;
  1164.     },
  1165.   
  1166.     undoRemoveSubBrowser : function(aIndex) 
  1167.     {
  1168.         if (!aIndex) aIndex = 0;
  1169.         var data = this.undoCache.getEntryAt(aIndex);
  1170.         if (!data) return;
  1171.         this.undoCache.removeEntryAt(aIndex);
  1172.         try {
  1173.             state = this.evalInSandbox('('+data.state+')');
  1174.             var box = gBrowser.boxObject;
  1175.             state = {
  1176.                 content : {
  1177.                     type   : 'root',
  1178.                     width  : (state.position & this.POSITION_HORIZONTAL ?
  1179.                                 Math.round(Math.max(1, box.width) / 2) :
  1180.                                 box.width ),
  1181.                     height : (state.position & this.POSITION_VERTICAL ?
  1182.                                 Math.round(Math.max(1, box.height) / 2) :
  1183.                                 box.height )
  1184.                 },
  1185.                 children : [state]
  1186.             };
  1187.             this.buildContent(state, document.getElementById('appcontent'));
  1188.         }
  1189.         catch(e) {
  1190.         }
  1191.     },
  1192.     
  1193.     get undoCache() 
  1194.     {
  1195.         if (!this._undoCache) {
  1196.             this._undoCache = {};
  1197.             Components.utils.import(
  1198.                 'resource://splitbrowser-modules/undoCache.js',
  1199.                 this._undoCache
  1200.             );
  1201.         }
  1202.         return this._undoCache.undoCache;
  1203.     },
  1204.     _undoCache : null,
  1205.  
  1206.     initUndoList : function(aPopup) 
  1207.     {
  1208.         this.undoCache.initUndoList(aPopup);
  1209.     },
  1210.   
  1211.     activeBrowserOpenTab : function() 
  1212.     {
  1213.         var b = this.activeBrowser;
  1214.         if (b == gBrowser)
  1215.             BrowserOpenTab();
  1216.         else
  1217.             b.parentSubBrowser.openNewTab();
  1218.     },
  1219.  
  1220.     activeBrowserCloseWindow : function() 
  1221.     {
  1222.         var b = this.activeBrowser;
  1223.         if (b == gBrowser) {
  1224.             if (this._browsers.length)
  1225.                 gBrowser.removeCurrentTab();
  1226.             else
  1227.                 BrowserCloseWindow();
  1228.         }
  1229.         else {
  1230.             b.parentSubBrowser.close();
  1231.         }
  1232.     },
  1233.  
  1234.     activeBrowserCloseTabOrWindow : function() 
  1235.     {
  1236.         var b = this.activeBrowser;
  1237.         if (b == gBrowser) {
  1238.             BrowserCloseTabOrWindow();
  1239.         }
  1240.         else {
  1241.             if (b.localName == 'tabbrowser' && this.getTabs(b).snapshotLength > 1)
  1242.                 b.removeTab(b.selectedTab);
  1243.             else
  1244.                 b.parentSubBrowser.close();
  1245.         }
  1246.     },
  1247.  
  1248.     activeBrowserTryToCloseWindow : function() 
  1249.     {
  1250.         var b = this.activeBrowser;
  1251.         if (b == gBrowser) {
  1252.             BrowserTryToCloseWindow();
  1253.         }
  1254.         else {
  1255.             b.parentSubBrowser.close();
  1256.         }
  1257.     },
  1258.  
  1259.     activeBrowserBack : function(aEvent, aIgnoreAlt) 
  1260.     {
  1261.         var b = this.activeBrowser;
  1262.         if (b == gBrowser) {
  1263.             return window.BrowserBack.apply(window, arguments);
  1264.         }
  1265.  
  1266.         var where = whereToOpenLink(aEvent, false, aIgnoreAlt);
  1267.         if (where == 'current') {
  1268.             try {
  1269.                 b.webNavigation.goBack();
  1270.             }
  1271.             catch(e) {
  1272.             }
  1273.         }
  1274.         else {
  1275.             var sessionHistory = b.webNavigation.sessionHistory;
  1276.             var currentIndex = sessionHistory.index;
  1277.             var entry = sessionHistory.getEntryAtIndex(currentIndex - 1, false);
  1278.             var url = entry.URI.spec;
  1279.             switch (where)
  1280.             {
  1281.                 case 'tab':
  1282.                 case 'tabshifted':
  1283.                     if (b.localName == 'tabbrowser') {
  1284.                         var loadInBackground = this.getPref('browser.tabs.loadBookmarksInBackground');
  1285.                         if (where == 'tabshifted') loadInBackground = !loadInBackground;
  1286.                         var t = b.addTab(url);
  1287.                         if (!loadInBackground) b.selectedTab = t;
  1288.                         return;
  1289.                     }
  1290.  
  1291.                 default:
  1292.                     openUILinkIn(url, where);
  1293.                     return;
  1294.             }
  1295.         }
  1296.  
  1297.         return;
  1298.     },
  1299.  
  1300.     activeBrowserForward : function(aEvent, aIgnoreAlt) 
  1301.     {
  1302.         var b = this.activeBrowser;
  1303.         if (b == gBrowser) {
  1304.             return window.BrowserForward.apply(window, arguments);
  1305.         }
  1306.  
  1307.         var where = whereToOpenLink(aEvent, false, aIgnoreAlt);
  1308.         if (where == 'current') {
  1309.             try {
  1310.                 b.webNavigation.goForward();
  1311.             }
  1312.             catch(e) {
  1313.             }
  1314.         }
  1315.         else {
  1316.             var sessionHistory = b.webNavigation.sessionHistory;
  1317.             var currentIndex = sessionHistory.index;
  1318.             var entry = sessionHistory.getEntryAtIndex(currentIndex + 1, false);
  1319.             var url = entry.URI.spec;
  1320.             switch (where)
  1321.             {
  1322.                 case 'tab':
  1323.                 case 'tabshifted':
  1324.                     if (b.localName == 'tabbrowser') {
  1325.                         var loadInBackground = this.getPref('browser.tabs.loadBookmarksInBackground');
  1326.                         if (where == 'tabshifted') loadInBackground = !loadInBackground;
  1327.                         var t = b.addTab(url);
  1328.                         if (!loadInBackground) b.selectedTab = t;
  1329.                         return;
  1330.                     }
  1331.  
  1332.                 default:
  1333.                     openUILinkIn(url, where);
  1334.                     return;
  1335.             }
  1336.         }
  1337.  
  1338.         return;
  1339.     },
  1340.  
  1341.     activeBrowserStop : function() 
  1342.     {
  1343.         const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
  1344.         this.activeBrowser.webNavigation.stop(nsIWebNavigation.STOP_ALL);
  1345.     },
  1346.  
  1347.     activeBrowserReload : function() 
  1348.     {
  1349.         const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
  1350.         this.activeBrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_NONE);
  1351.     },
  1352.  
  1353.     activeBrowserReloadSkipCache : function() 
  1354.     {
  1355.         const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
  1356.         this.activeBrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
  1357.     },
  1358.  
  1359.     activeBrowserReloadWithFlags : function(aReloadFlags) 
  1360.     {
  1361.         var webNav = this.activeBrowser.webNavigation;
  1362.         try {
  1363.             var sh = webNav.sessionHistory;
  1364.             if (sh)
  1365.                 webNav = sh.QueryInterface(nsIWebNavigation);
  1366.         }
  1367.         catch (e) {
  1368.         }
  1369.  
  1370.         try {
  1371.             webNav.reload(aReloadFlags);
  1372.         }
  1373.         catch (e) {
  1374.         }
  1375.     },
  1376.  
  1377.     activeBrowserFocusURLBar : function() 
  1378.     {
  1379.         if (this.activeBrowser &&
  1380.             this.activeBrowser.parentSubBrowser &&
  1381.             this.activeBrowser.parentSubBrowser.localName == 'subbrowser') {
  1382.             var b = this.activeBrowser.parentSubBrowser;
  1383.             b.toggleNavigation(true);
  1384.             b.urlbar.focus();
  1385.         }
  1386.         else if (gURLBar) {
  1387.             gURLBar.focus();
  1388.         }
  1389.     },
  1390.  
  1391.     activeBrowserSelectURLBar : function() 
  1392.     {
  1393.         if (this.activeBrowser &&
  1394.             this.activeBrowser.parentSubBrowser &&
  1395.             this.activeBrowser.parentSubBrowser.localName == 'subbrowser') {
  1396.             var b = this.activeBrowser.parentSubBrowser;
  1397.             b.toggleNavigation(true);
  1398.             b.urlbar.select();
  1399.         }
  1400.         else if (gURLBar) {
  1401.             gURLBar.select();
  1402.         }
  1403.     },
  1404.  
  1405.     activeBrowserSavePage : function() 
  1406.     {
  1407.         saveDocument(this.activeBrowser.contentDocument);
  1408.     },
  1409.  
  1410.     activeBrowserViewPageSource : function() 
  1411.     {
  1412.         BrowserViewSourceOfDocument(this.activeBrowser.contentDocument);
  1413.     },
  1414.  
  1415.     activeBrowserViewPageInfo : function() 
  1416.     {
  1417.         BrowserPageInfo(this.activeBrowser.contentDocument);
  1418.     },
  1419.  
  1420.     activeBrowserAddBookmarkAs : function() 
  1421.     {
  1422.         if ('PlacesCommandHook' in window) // Firefox 3
  1423.             PlacesCommandHook.bookmarkPage(this.activeBrowser.selectedBrowser, PlacesUtils.bookmarksMenuFolderId, true);
  1424.         else // Firefox 2
  1425.             addBookmarkAs(this.activeBrowser, false);
  1426.     },
  1427.  
  1428.     activeBrowserBookmarkAllTabs : function() 
  1429.     {
  1430.         var b = this.activeBrowser;
  1431.         if (b.localName != 'tabbrowser') b = gBrowser;
  1432.         if ('PlacesUIUtils' in window) { // Firefox 3
  1433.             if (b == gBrowser) {
  1434.                 gBookmarkAllTabsHandler.doCommand();
  1435.                 return;
  1436.             }
  1437.             else {
  1438.                 var done = {};
  1439.                 PlacesUIUtils.showMinimalAddMultiBookmarkUI(
  1440.                     this.getTabsArray(b)
  1441.                         .map(function(aTab) {
  1442.                             return aTab.linkedBrowser.currentURI;
  1443.                         })
  1444.                         .filter(function(aURI) {
  1445.                             if (aURI.spec in done) return false;
  1446.                             done[aURI.spec] = true;
  1447.                             return true;
  1448.                         })
  1449.                 );
  1450.             }
  1451.         }
  1452.         else {
  1453.             addBookmarkAs(b, true);
  1454.         }
  1455.     },
  1456.   
  1457. /* save / load */ 
  1458.     
  1459.     get SessionStore() { 
  1460.         if (!this._SessionStore) {
  1461.             this._SessionStore = Components.classes['@mozilla.org/browser/sessionstore;1'].getService(Components.interfaces.nsISessionStore);
  1462.         }
  1463.         return this._SessionStore;
  1464.     },
  1465.     _SessionStore : null,
  1466.  
  1467.     save : function(aCount) 
  1468.     {
  1469.         try {
  1470.             var state = this.getContainerState(document.getElementById('appcontent'));
  1471.             state = state.toSource();
  1472.             if (this.useSessionStore)
  1473.                 this.SessionStore.setWindowValue(window, 'splitbrowser.state', state);
  1474.             else
  1475.                 this.setPref('splitbrowser.state', state);
  1476.         }
  1477.         catch(e) {
  1478.             // try again!
  1479.             if (!aCount)
  1480.                 aCount = 0;
  1481.             else if (aCount > 10)
  1482.                 return;
  1483.             window.setTimeout(function(aSelf) {
  1484.                 aSelf.save(aCount);
  1485.             }, 1000, this, aCount++);
  1486.         }
  1487.     },
  1488.     
  1489.     getContainerState : function(aContainer) 
  1490.     {
  1491.         var state = {};
  1492.         state.children = [];
  1493.  
  1494.         var hContainer = aContainer.hContainer;
  1495.         if (hContainer) {
  1496.             var wrapper = aContainer.contentWrapper;
  1497.             var originalContent = hContainer.firstChild;
  1498.             for (var i = 0, maxi = hContainer.childNodes.length; i < maxi; i++)
  1499.             {
  1500.                 if ((wrapper && hContainer.childNodes[i] == wrapper) ||
  1501.                     hContainer.childNodes[i].localName == 'subbrowser') {
  1502.                     originalContent = hContainer.childNodes[i];
  1503.                     break;
  1504.                 }
  1505.             }
  1506.             if (originalContent.localName == 'subbrowser') {
  1507.                 state.content = this.serializeSubBrowserState(originalContent);
  1508.                 state.content.type       = 'subbrowser';
  1509.                 state.content.lastWidth  = aContainer.lastwidth;
  1510.                 state.content.lastHeight = aContainer.lastheight;
  1511.             }
  1512.             else if (wrapper && hContainer.childNodes[i] == wrapper) {
  1513.                 state.content = {
  1514.                     type       : 'root',
  1515.                     width      : gBrowser.boxObject.width,
  1516.                     height     : gBrowser.boxObject.height
  1517.                 };
  1518.             }
  1519.             else {
  1520.                 state.children.push(this.getContainerState(originalContent));
  1521.                 state.children[state.children.length-1].position = this.POSITION_RIGHT;
  1522.                 state.children[state.children.length-1].width    = originalContent.boxObject.width;
  1523.             }
  1524.  
  1525.             var node = originalContent.previousSibling;
  1526.             while (node)
  1527.             {
  1528.                 if (node.localName == 'splitter') {
  1529.                     node = node.previousSibling;
  1530.                     continue;
  1531.                 }
  1532.                 state.children.push(this.getContainerState(node));
  1533.                 state.children[state.children.length-1].position = this.POSITION_LEFT;
  1534.                 state.children[state.children.length-1].width    = node.boxObject.width;
  1535.                 if (node.nextSibling.getAttribute('state') == 'collapsed')
  1536.                     state.children[state.children.length-1].collapsed = true;
  1537.                 node = node.previousSibling;
  1538.             }
  1539.  
  1540.             var node = originalContent.nextSibling;
  1541.             while (node)
  1542.             {
  1543.                 if (node.localName == 'splitter') {
  1544.                     node = node.nextSibling;
  1545.                     continue;
  1546.                 }
  1547.                 state.children.push(this.getContainerState(node));
  1548.                 state.children[state.children.length-1].position = this.POSITION_RIGHT;
  1549.                 state.children[state.children.length-1].width    = node.boxObject.width;
  1550.                 if (node.previousSibling.getAttribute('state') == 'collapsed')
  1551.                     state.children[state.children.length-1].collapsed = true;
  1552.                 node = node.nextSibling;
  1553.             }
  1554.         }
  1555.  
  1556.         var vContainer = aContainer.vContainer;
  1557.  
  1558.         var originalContent = vContainer.firstChild;
  1559.         for (var i = 0, maxi = vContainer.childNodes.length; i < maxi; i++)
  1560.         {
  1561.             if (vContainer.childNodes[i] == hContainer ||
  1562.                 vContainer.childNodes[i].localName == 'subbrowser') {
  1563.                 originalContent = vContainer.childNodes[i];
  1564.                 break;
  1565.             }
  1566.         }
  1567.         if (originalContent.localName == 'subbrowser') {
  1568.             state.content = this.serializeSubBrowserState(originalContent);
  1569.             state.content.type       = 'subbrowser';
  1570.             state.content.lastWidth  = aContainer.lastwidth;
  1571.             state.content.lastHeight = aContainer.lastheight;
  1572.         }
  1573.         else if (!state.content) {
  1574.             state.children.push(this.getContainerState(originalContent));
  1575.             state.children[state.children.length-1].position = this.POSITION_BOTTOM;
  1576.             state.children[state.children.length-1].height   = originalContent.boxObject.height;
  1577.         }
  1578.  
  1579.         var node = originalContent.previousSibling;
  1580.         while (node)
  1581.         {
  1582.             if (node.localName == 'splitter') {
  1583.                 node = node.previousSibling;
  1584.                 continue;
  1585.             }
  1586.             state.children.push(this.getContainerState(node));
  1587.             state.children[state.children.length-1].position = this.POSITION_TOP;
  1588.             state.children[state.children.length-1].height   = node.boxObject.height;
  1589.             if (node.nextSibling.getAttribute('state') == 'collapsed')
  1590.                 state.children[state.children.length-1].collapsed = true;
  1591.             node = node.previousSibling;
  1592.         }
  1593.  
  1594.         var node = originalContent.nextSibling;
  1595.         while (node)
  1596.         {
  1597.             if (node.localName == 'splitter') {
  1598.                 node = node.nextSibling;
  1599.                 continue;
  1600.             }
  1601.             state.children.push(this.getContainerState(node));
  1602.             state.children[state.children.length-1].position = this.POSITION_BOTTOM;
  1603.             state.children[state.children.length-1].height   = node.boxObject.height;
  1604.             if (node.previousSibling.getAttribute('state') == 'collapsed')
  1605.                 state.children[state.children.length-1].collapsed = true;
  1606.             node = node.nextSibling;
  1607.         }
  1608.  
  1609.         return state;
  1610.     },
  1611.  
  1612.     serializeSubBrowserState : function(aBrowser) 
  1613.     {
  1614.         var state = this.serializeBrowserState(aBrowser.browser);
  1615.  
  1616.         state.uri         = aBrowser.src;
  1617.         state.width       = aBrowser.boxObject.width;
  1618.         state.height      = aBrowser.boxObject.height;
  1619.         state.collapsed   = aBrowser.contentCollapsed;
  1620.         state.toolbarMode = (aBrowser.getAttribute('toolbar-mode') == 'vertical' ? 'vertical' : 'horizontal' );
  1621.         state.syncScroll  = aBrowser.syncScroll;
  1622.         state.name        = aBrowser.name;
  1623.  
  1624.         return state;
  1625.     },
  1626.  
  1627.     serializeBrowserState : function(aBrowser) 
  1628.     {
  1629.         var state = {
  1630.                 textZoom    : [aBrowser.markupDocumentViewer.textZoom],
  1631.                 histories   : this.serializeBrowserSessionHistories(aBrowser)
  1632.             };
  1633.  
  1634.         if (aBrowser.localName == 'tabbrowser') {
  1635.             var tabs = this.getTabs(aBrowser);
  1636.             var tab;
  1637.             for (var i = 0, maxi = tabs.snapshotLength; i < maxi; i++)
  1638.             {
  1639.                 tab = tabs.snapshotItem(i);
  1640.                 state.textZoom[i] = tab.linkedBrowser.markupDocumentViewer.textZoom;
  1641.                 if (tab == aBrowser.selectedTab) {
  1642.                     state.selectedTab = i;
  1643.                 }
  1644.             }
  1645.         }
  1646.  
  1647.         return state;
  1648.     },
  1649.  
  1650.     // maximal amount of POSTDATA to be stored (in bytes, -1 = all of it) 
  1651.     DEFAULT_POSTDATA : 0,
  1652.  
  1653.     // on which sites to save text data, POSTDATA and cookies 
  1654.     // (0 = everywhere, 1 = unencrypted sites, 2 = nowhere) default = 1
  1655.     PRIVACY_NONE      : 0,
  1656.     PRIVACY_ENCRYPTED : 1,
  1657.     PRIVACY_FULL      : 2,
  1658.  
  1659.     checkPrivacyLevel : function sss_checkPrivacyLevel(aIsHTTPS) 
  1660.     {
  1661.         return this.getPref('sessionstore.privacy_level', this.PRIVACY_ENCRYPTED) < (aIsHTTPS ? this.PRIVACY_ENCRYPTED : this.PRIVACY_FULL );
  1662.     },
  1663.  
  1664.     serializeBrowserSessionHistories : function(aBrowser) 
  1665.     {
  1666.         var histories = [];
  1667.         if (aBrowser.localName == 'tabbrowser') {
  1668.             var tabs = this.getTabs(aBrowser);
  1669.             for (var i = 0, maxi = tabs.snapshotLength; i < maxi; i++)
  1670.             {
  1671.                 histories.push(this.serializeSessionHistory(aBrowser.getBrowserForTab(tabs.snapshotItem(i))));
  1672.             }
  1673.         }
  1674.         else {
  1675.             histories.push(this.serializeSessionHistory(aBrowser));
  1676.         }
  1677.         return histories
  1678.     },
  1679.     
  1680.     serializeSessionHistory : function(aBrowser) 
  1681.     {
  1682.         var SH = null;
  1683.         try {
  1684.             SH = aBrowser.sessionHistory;
  1685.         }
  1686.         catch(e) {
  1687.         }
  1688.  
  1689.         var entries = [],
  1690.             entry,
  1691.             x       = {},
  1692.             y       = {},
  1693.             content;
  1694.         if (SH)
  1695.             for (var i = 0, maxi = SH.count; i < maxi; i++)
  1696.             {
  1697.                 entry = this.serializeHistoryEntry(SH.getEntryAtIndex(i, false));
  1698.                 if (entry) {
  1699.                     if (i == SH.index)
  1700.                         this.storePosition(aBrowser.contentWindow, entry);
  1701.                     entries.push(entry);
  1702.                 }
  1703.             }
  1704.  
  1705.         return {
  1706.             entries   : entries,
  1707.             index     : (SH ? SH.index : -1 ),
  1708.             userInput : this.serializeUserInput(aBrowser.contentWindow)
  1709.         };
  1710.     },
  1711.     
  1712.     storePosition : function(aFrame, aEntry) 
  1713.     {
  1714.         aEntry.x = aFrame.scrollX;
  1715.         aEntry.y = aFrame.scrollY;
  1716.  
  1717.         var frames = aFrame.frames;
  1718.         if (frames.length && aEntry.children && aEntry.children.length) {
  1719.             for (var i = 0, maxi = frames.length; i < maxi; i++)
  1720.             {
  1721.                 if (i in aEntry.children)
  1722.                     this.storePosition(frames[i], aEntry.children[i]);
  1723.             }
  1724.         }
  1725.     },
  1726.  
  1727.     serializeHistoryEntry : function(aEntry) 
  1728.     {
  1729.         if (!aEntry) return null;
  1730.  
  1731.         aEntry = aEntry.QueryInterface(Components.interfaces.nsIHistoryEntry);
  1732.         aEntry = aEntry.QueryInterface(Components.interfaces.nsISHEntry);
  1733.  
  1734.         var x = {}, y = {};
  1735.         aEntry.getScrollPosition(x, y);
  1736.  
  1737.         var data = {
  1738.             id         : aEntry.ID, // to compare with saved data
  1739.             uri        : (aEntry.URI ? aEntry.URI.spec : null ),
  1740.             title      : aEntry.title,
  1741.             isSubFrame : aEntry.isSubFrame,
  1742.             x          : Math.max(x.value, 0),
  1743.             y          : Math.max(y.value, 0),
  1744.             children   : []
  1745.         };
  1746.  
  1747.         if ('cacheKey' in aEntry && aEntry.cacheKey) {
  1748.             data.cacheKey = aEntry.cacheKey.QueryInterface(Components.interfaces.nsISupportsPRUint32).data;
  1749.         }
  1750.         else {
  1751.             data.cacheKey = 0;
  1752.         }
  1753.  
  1754.         // get post data
  1755.         try {
  1756.             var prefPostdata = this.getPref('sessionstore.postdata', this.DEFAULT_POSTDATA);
  1757.             if (prefPostdata && aEntry.postData && this.checkPrivacyLevel(aEntry.URI.schemeIs('https'))) {
  1758.                 aEntry.postData
  1759.                         .QueryInterface(Components.interfaces.nsISeekableStream)
  1760.                         .seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, 0);
  1761.                 var stream = Components.classes['@mozilla.org/scriptableinputstream;1']
  1762.                         .createInstance(Components.interfaces.nsIScriptableInputStream);
  1763.                 stream.init(aEntry.postData);
  1764.                 var postdata = stream.read(stream.available());
  1765.                 if (prefPostdata == -1 || postdata.replace(/^(Content-.*\r\n)+(\r\n)*/, '').length <= prefPostdata) {
  1766.                     data.postdata = postdata;
  1767.                 }
  1768.             }
  1769.         }
  1770.         catch (e) {
  1771.         }
  1772.  
  1773.         var children = [];
  1774.         try {
  1775.             aEntry = aEntry.QueryInterface(Components.interfaces.nsISHContainer);
  1776.         }
  1777.         catch(e) {
  1778.             return data;
  1779.         }
  1780.  
  1781.         for (var i = 0, maxi = aEntry.childCount; i < maxi; i++)
  1782.         {
  1783.             data.children.push(this.serializeHistoryEntry(aEntry.GetChildAt(i)));
  1784.         }
  1785.         return data;
  1786.     },
  1787.  
  1788.     serializeUserInput : function(aFrame) 
  1789.     {
  1790.         var data = {};
  1791.  
  1792.         var isHTTPS = aFrame.location.href.indexOf('https://') == 0;
  1793.  
  1794.         if ((aFrame.document.designMode || '') == 'on') {
  1795.             if (this.checkPrivacyLevel(isHTTPS))
  1796.                 data.innerHTML = aFrame.document.body.innerHTML;
  1797.         }
  1798.         else if (this.checkPrivacyLevel(isHTTPS)) {
  1799.             try {
  1800.                 var xpathResult = aFrame.document.evaluate(
  1801.                         'descendant::xul:textbox | descendant::*[local-name() = "input" or local-name() = "INPUT" or local-name() = "textarea" or local-name() = "TEXTAREA"]',
  1802.                         aFrame.document,
  1803.                         this.NSResolver,
  1804.                         XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
  1805.                         null
  1806.                     );
  1807.                 if (xpathResult.snapshotLength) {
  1808.                     var text = [];
  1809.                     var node;
  1810.                     for (var i = 0, maxi = xpathResult.snapshotLength; i < maxi; i++)
  1811.                     {
  1812.                         node = xpathResult.snapshotItem(i);
  1813.                         if (node.wrappedJSObject) node = node.wrappedJSObject;
  1814.                         switchByName:
  1815.                         switch (node.localName.toLowerCase())
  1816.                         {
  1817.                             case 'input':
  1818.                                 if (/^(true|readonly|disabled)$/i.test(node.getAttribute('readonly') || node.getAttribute('disabled') || ''))
  1819.                                         break switchByName;
  1820.  
  1821.                                 switchByType:
  1822.                                 switch ((node.getAttribute('type') || '').toLowerCase())
  1823.                                 {
  1824.                                     case 'checkbox':
  1825.                                         text.push((node.id ? '#id:'+encodeURIComponent(node.id) : '#name:'+encodeURIComponent(node.name) )+'='+(node.checked ? true : false ));
  1826.                                         break switchByName;
  1827.  
  1828.                                     case 'radio':
  1829.                                         if (node.checked )
  1830.                                             text.push('#name:'+encodeURIComponent(node.name)+'='+encodeURIComponent(node.value));
  1831.                                         break switchByName;
  1832.  
  1833.                                     case 'submit':
  1834.                                     case 'reset':
  1835.                                     case 'button':
  1836.                                     case 'image':
  1837.                                         break switchByName;
  1838.  
  1839.                                     default:
  1840.                                         break switchByType;
  1841.                                 }
  1842.                             case 'textbox':
  1843.                             case 'textarea':
  1844.                                 if (node.value)
  1845.                                     text.push((node.id ? '#id:'+encodeURIComponent(node.id) : '#name:'+encodeURIComponent(node.name) )+'='+encodeURIComponent(node.value));
  1846.                                 break;
  1847.  
  1848.                             default:
  1849.                                 break;
  1850.                         }
  1851.                     }
  1852.                     if (text.length)
  1853.                         data.text = text.join(' ');
  1854.                 }
  1855.             }
  1856.             catch(e) {
  1857. dump(e+'\n');
  1858.             }
  1859.         }
  1860.  
  1861.         var frames = aFrame.frames;
  1862.         if (frames.length) {
  1863.             data.children = [];
  1864.             for (var i = 0, maxi = frames.length; i < maxi; i++)
  1865.             {
  1866.                 data.children.push(this.serializeUserInput(frames[i]));
  1867.             }
  1868.         }
  1869.  
  1870.         return data;
  1871.     },
  1872.     
  1873.     saveWithDelay : function() 
  1874.     {
  1875.         if (!this.useSessionStore || !this.canSave) return;
  1876.         if (this.saveWithDelayTimer) {
  1877.             window.clearTimeout(this.saveWithDelayTimer);
  1878.             this.saveWithDelayTimer = null;
  1879.         }
  1880.         this.saveWithDelayTimer = window.setTimeout(function(aSelf) {
  1881.             aSelf.save();
  1882.         }, 1000, this);
  1883.     },
  1884.  
  1885.     load : function() 
  1886.     {
  1887.         var state = this.SessionStore.getWindowValue(window, 'splitbrowser.state') || this.getPref('splitbrowser.state');
  1888.         if (this.useSessionStore) {
  1889.             this.clearPref('splitbrowser.state');
  1890.             this.SessionStore.setWindowValue(window, 'splitbrowser.state', '');
  1891.         }
  1892.         if (!state) return;
  1893.         try {
  1894.             state = this.evalInSandbox('('+state+')');
  1895.         }
  1896.         catch(e) {
  1897.             Application.console.log(e+'\n\n'+state);
  1898.             return;
  1899.         }
  1900.  
  1901.         this.buildContent(state, document.getElementById('appcontent'));
  1902.     },
  1903.     
  1904.     buildContent : function(aState, aContainer) 
  1905.     {
  1906.         var content;
  1907.         if (aState.content && aState.content.type) {
  1908.             switch (aState.content.type)
  1909.             {
  1910.                 case 'root':
  1911.                     aContainer.contentWrapper.width  = aState.content.width;
  1912.                     aContainer.contentWrapper.height = aState.content.height;
  1913.                     content = aContainer.contentWrapper;
  1914.                     break;
  1915.  
  1916.                 case 'subbrowser':
  1917.                     if (aState.content.history) {
  1918.                         aState.content.histories = [aState.content.history];
  1919.                     }
  1920.  
  1921.                     var b = this.createSubBrowser(
  1922.                             aState.content.histories && aState.content.histories.length ? null : aState.content.uri
  1923.                         );
  1924.                     aContainer.hContainer.appendChild(b);
  1925.                     aContainer.hContainer.width  = aState.content.width;
  1926.                     aContainer.hContainer.height = aState.content.height;
  1927.  
  1928.                     this.deserializeBrowserState(b.browser, aState.content);
  1929.  
  1930.                     aContainer.lastwidth  = aState.content.lastWidth;
  1931.                     aContainer.lastheight = aState.content.lastHeight;
  1932.     /*
  1933.                     if (aState.content.collapsed) {
  1934.                         if (aState.position & this.POSITION_HORIZONTAL)
  1935.                             aContainer.setAttribute('maxwidth', 0);
  1936.                         else
  1937.                             aContainer.setAttribute('maxheight', 0);
  1938.                     }
  1939.     */
  1940.  
  1941.                     if (aState.content.toolbarMode)
  1942.                         b.setAttribute('toolbar-mode', aState.content.toolbarMode);
  1943.  
  1944.                     if (aState.content.syncScroll)
  1945.                         b.setAttribute('sync-scroll', aState.content.syncScroll);
  1946.                     else
  1947.                         b.removeAttribute('sync-scroll');
  1948.  
  1949.                     if (aState.content.name)
  1950.                         b.setAttribute('name', aState.content.name);
  1951.                     else
  1952.                         b.removeAttribute('name');
  1953.  
  1954.                     content = b;
  1955.                     break;
  1956.  
  1957.                 default:
  1958.                     break;
  1959.             }
  1960.         }
  1961.         if (!content) {
  1962.             content = document.createElement('spacer');
  1963.             content.setAttribute('flex', 1);
  1964.             aContainer.hContainer.appendChild(content);
  1965.         }
  1966.  
  1967.         var container;
  1968.         var spacer = document.createElement('spacer');
  1969.         spacer.setAttribute('flex', 1);
  1970.         aState.children.forEach(function(aChild) {
  1971.             container = this.addContainerTo(
  1972.                 aContainer,
  1973.                 aChild.position,
  1974.                 content,
  1975.                 aChild.width,
  1976.                 aChild.height
  1977.             );
  1978.             if (aChild.collapsed)
  1979.                 (aChild.position & this.POSITION_BEFORE ? container.nextSibling : container.previousSibling).setAttribute('state', 'collapsed');
  1980.             this.buildContent(aChild, container);
  1981.         }, this);
  1982.  
  1983.         if (content && content.localName == 'spacer') {
  1984.             if (content.nextSibling)
  1985.                 aContainer.hContainer.removeChild(content.nextSibling);
  1986.             else if (content.previousSibling)
  1987.                 aContainer.hContainer.removeChild(content.previousSibling);
  1988.             aContainer.hContainer.removeChild(content);
  1989.             this.cleanUpContainer(aContainer);
  1990.         }
  1991.     },
  1992.  
  1993.     deserializeBrowserState : function(aBrowser, aBrowserState, aCallback) 
  1994.     {
  1995.         var browser, tab;
  1996.         if (aBrowser.localName == 'tabbrowser') {
  1997.             browser = aBrowser.mCurrentBrowser,
  1998.             tab     = aBrowser.mCurrentTab;
  1999.         }
  2000.         else {
  2001.             browser = aBrowser;
  2002.             tab     = null;
  2003.         }
  2004.  
  2005.         try {
  2006.             browser.sessionHistory.QueryInterface(Components.interfaces.nsISHistoryInternal);
  2007.         }
  2008.         catch(e) {
  2009.             window.setTimeout(arguments.callee, 50, aBrowser, aBrowserState);
  2010.             dump(e+'\n');
  2011.             return;
  2012.         }
  2013.  
  2014.         if (aBrowserState.histories) {
  2015.             aBrowserState.histories.forEach(function(aHistory, aIndex) {
  2016.                 if (aIndex) {
  2017.                     tab     = aBrowser.addTab('about:blank');
  2018.                     browser = aBrowser.getBrowserForTab(tab);
  2019.                 }
  2020.                 SplitBrowser.deserializeSessionHistory(browser, aHistory);
  2021.  
  2022.                 if (aBrowserState.textZoom[aIndex])
  2023.                     browser.markupDocumentViewer.textZoom = aBrowserState.textZoom[aIndex];
  2024.  
  2025.                 if (aBrowser.localName == 'tabbrowser' &&
  2026.                     aBrowserState.selectedTab == aIndex)
  2027.                     aBrowser.selectedTab = tab;
  2028.             });
  2029.         }
  2030.  
  2031.         if (aCallback && typeof aCallback == 'function')
  2032.             aCallback();
  2033.     },
  2034.     
  2035.     deserializeSessionHistory : function(aBrowser, aData) 
  2036.     {
  2037.         var SHInternal = aBrowser.sessionHistory.QueryInterface(Components.interfaces.nsISHistoryInternal);
  2038.         aData.entries.forEach(function(aEntry) {
  2039.             SHInternal.addEntry(SplitBrowser.deserializeSessionHistoryEntry(aEntry), true);
  2040.         });
  2041.  
  2042.         aBrowser.addEventListener('DOMContentLoaded', function() {
  2043.             aBrowser.removeEventListener('DOMContentLoaded', arguments.callee, false);
  2044.             SplitBrowser.restorePosition(aBrowser.contentWindow, aData.entries[aData.index]);
  2045.             SplitBrowser.deserializeUserInput(aBrowser.contentWindow, aData.userInput);
  2046.         }, false);
  2047.  
  2048.         try {
  2049.             aBrowser.gotoIndex(aData.index);
  2050.         }
  2051.         catch(e) { // when the entry is moving in frames...
  2052.             try {
  2053.                 aBrowser.gotoIndex(aBrowser.sessionHistory.count-1);
  2054.             }
  2055.             catch(ex) { // when there is no history, do nothing
  2056.             }
  2057.         }
  2058.     },
  2059.     
  2060.     restorePosition : function(aFrame, aEntry) 
  2061.     {
  2062.         aFrame.scrollTo(aEntry.x, aEntry.y);
  2063.  
  2064.         var frames = aFrame.frames;
  2065.         if (frames.length && aEntry.children && aEntry.children.length) {
  2066.             for (var i = 0, maxi = frames.length; i < maxi; i++)
  2067.             {
  2068.                 if (i in aEntry.children)
  2069.                     this.restorePosition(frames[i], aEntry.children[i]);
  2070.             }
  2071.         }
  2072.     },
  2073.  
  2074.     deserializeSessionHistoryEntry : function(aData) 
  2075.     {
  2076.         var entry = Components.classes['@mozilla.org/browser/session-history-entry;1'].createInstance(Components.interfaces.nsISHEntry);
  2077.         entry = entry.QueryInterface(Components.interfaces.nsIHistoryEntry);
  2078.  
  2079.         entry.setURI(this.makeURIFromSpec(aData.uri));
  2080.         entry.setTitle(aData.title);
  2081.         entry.setIsSubFrame(aData.isSubFrame);
  2082.         entry.loadType = Components.interfaces.nsIDocShellLoadInfo.loadHistory;
  2083.  
  2084.         entry.setScrollPosition(aData.x, aData.y);
  2085.  
  2086.  
  2087.         if ('cacheKey' in aData && aData.cacheKey) {
  2088.             var cacheKey = Components.classes['@mozilla.org/supports-PRUint32;1'].createInstance(Components.interfaces.nsISupportsPRUint32);
  2089.             cacheKey.type = cacheKey.TYPE_PRUINT32;
  2090.             cacheKey.data = parseInt(aData.cacheKey);
  2091.             cacheKey = cacheKey.QueryInterface(Components.interfaces.nsISupports);
  2092.  
  2093.             entry.cacheKey         = cacheKey;
  2094.             entry.expirationStatus = 'expirationStatus' in aData ? aData.expirationStatus : null ;
  2095.         }
  2096.  
  2097.         if ('postdata' in aData && aData.postdata) {
  2098.             var stream = Components.classes['@mozilla.org/io/string-input-stream;1']
  2099.                     .createInstance(Components.interfaces.nsIStringInputStream);
  2100.             stream.setData(aEntry.postdata, -1);
  2101.             entry.postData = stream;
  2102.         }
  2103.  
  2104.         if (!aData.children || !aData.children.length) return entry;
  2105.  
  2106.         entry = entry.QueryInterface(Components.interfaces.nsISHContainer);
  2107.         aData.children.forEach(function(aChild, aIndex) {
  2108.             entry.AddChild(SplitBrowser.deserializeSessionHistoryEntry(aChild), aIndex);
  2109.         });
  2110.  
  2111.         return entry;
  2112.     },
  2113.  
  2114.     deserializeUserInput : function(aFrame, aData) 
  2115.     {
  2116.         var isHTTPS = aFrame.location.href.indexOf('https://') == 0;
  2117.  
  2118.         if (aData.innerHTML) {
  2119.             aFrame.document.body.innerHTML = data.innerHTML;
  2120.         }
  2121.         else if (aData.text) {
  2122.             try {
  2123.                 var xpathResult = aFrame.document.evaluate(
  2124.                         'descendant::xul:textbox | descendant::*[local-name() = "input" or local-name() = "INPUT" or local-name() = "textarea" or local-name() = "TEXTAREA"]',
  2125.                         aFrame.document,
  2126.                         this.NSResolver,
  2127.                         XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
  2128.                         null
  2129.                     );
  2130.                 if (xpathResult.snapshotLength) {
  2131.                     var node;
  2132.                     var pos;
  2133.                     var data;
  2134.                     for (var i = 0, maxi = xpathResult.snapshotLength; i < maxi; i++)
  2135.                     {
  2136.                         node = xpathResult.snapshotItem(i);
  2137.                         if (node.wrappedJSObject) node = node.wrappedJSObject;
  2138.                         switchByName:
  2139.                         switch (node.localName.toLowerCase())
  2140.                         {
  2141.                             case 'input':
  2142.                                 if (/^(true|readonly|disabled)$/i.test(node.getAttribute('readonly') || node.getAttribute('disabled') || ''))
  2143.                                         break switchByName;
  2144.  
  2145.                                 switchByType:
  2146.                                 switch ((node.getAttribute('type') || '').toLowerCase())
  2147.                                 {
  2148.                                     case 'checkbox':
  2149.                                         if (
  2150.                                             (node.id && (pos = aData.text.indexOf('#id:'+encodeURIComponent(node.id)+'=')) > -1) ||
  2151.                                             (node.name && (pos = aData.text.indexOf('#name:'+encodeURIComponent(node.name))) > -1)
  2152.                                             ) {
  2153.                                             data = aData.text.substring(pos);
  2154.                                             if ((pos = data.indexOf(' ')) > -1)
  2155.                                                 data = data.substring(data.indexOf('=')+1, pos);
  2156.                                             else
  2157.                                                 data = data.substring(data.indexOf('=')+1, data.length);
  2158.                                             node.checked = (data == 'true');
  2159.                                         }
  2160.                                         break switchByName;
  2161.  
  2162.                                     case 'radio':
  2163.                                         if (
  2164.                                             (node.name && (pos = aData.text.indexOf('#name:'+encodeURIComponent(node.name)+'='+encodeURIComponent(node.value))) > -1)
  2165.                                             ) {
  2166.                                             if ((pos = aData.text.indexOf(' ')) > -1)
  2167.                                                 data = data.substring(data.indexOf('=')+1, pos);
  2168.                                             else
  2169.                                                 data = data.substring(data.indexOf('=')+1, data.length);
  2170.                                             if (decodeURIComponent(data) == node.value)
  2171.                                                 node.checked = true;
  2172.                                             else
  2173.                                                 node.checked = false;
  2174.                                         }
  2175.                                         else
  2176.                                             node.checked = false;
  2177.                                         break switchByName;
  2178.  
  2179.                                     case 'submit':
  2180.                                     case 'reset':
  2181.                                     case 'button':
  2182.                                     case 'image':
  2183.                                         break switchByName;
  2184.  
  2185.                                     default:
  2186.                                         break switchByType;
  2187.                                 }
  2188.  
  2189.                             case 'textbox':
  2190.                             case 'textarea':
  2191.                                 if (
  2192.                                     (node.id && (pos = aData.text.indexOf('#id:'+encodeURIComponent(node.id)+'=')) > -1) ||
  2193.                                     (node.name && (pos = aData.text.indexOf('#name:'+encodeURIComponent(node.name)+'=')) > -1)
  2194.                                     ) {
  2195.                                     data = aData.text.substring(pos);
  2196.                                     if ((pos = data.indexOf(' ')) > -1)
  2197.                                         data = data.substring(data.indexOf('=')+1, pos);
  2198.                                     else
  2199.                                         data = data.substring(data.indexOf('=')+1, data.length);
  2200.                                     node.value = decodeURIComponent(data);
  2201.                                 }
  2202.                                 break;
  2203.  
  2204.                             default:
  2205.                                 break;
  2206.                         }
  2207.                     }
  2208.                 }
  2209.             }
  2210.             catch(e) {
  2211.             }
  2212.         }
  2213.  
  2214.         var frames = aFrame.frames;
  2215.         if (frames.length && aData.children && aData.children.length) {
  2216.             for (var i = 0, maxi = frames.length; i < maxi; i++)
  2217.             {
  2218.                 if (i in aData.children)
  2219.                     this.deserializeUserInput(frames[i], aData.children[i]);
  2220.             }
  2221.         }
  2222.     },
  2223.      
  2224. /* popup-buttons */ 
  2225.     addButtonIsShown : false,
  2226.     
  2227.     get addButton() { 
  2228.         return document.getElementById('splitbrowser-add-button-in-panel');
  2229.     },
  2230.  
  2231.     get addButtonSize() { 
  2232.         return this.getPref('splitbrowser.appearance.addbuttons.size');
  2233.     },
  2234.     get addButtonAreaSize() {
  2235.         return this.getPref('splitbrowser.appearance.addbuttons.area');
  2236.     },
  2237.     get addButtonShowDelay() {
  2238.         return this.getPref('splitbrowser.delay.addbuttons.show');
  2239.     },
  2240.     get addButtonHideDelay() {
  2241.         return this.getPref('splitbrowser.delay.addbuttons.hide');
  2242.     },
  2243.     get addButtonFadeDuration() {
  2244.         return this.getPref('splitbrowser.delay.addbuttons.fade');
  2245.     },
  2246.  
  2247.     showAddButton : function(aEvent) 
  2248.     {
  2249.         if (this.addButtonIsShown) {
  2250.             if (this.hideAddButtonTimer)
  2251.                 this.stopDelayedHideAddButtonTimer();
  2252.             this.delayedHideAddButton();
  2253.         }
  2254.  
  2255.         var node = aEvent.targetSubBrowser;
  2256.  
  2257.         if (!(
  2258.             node.mIsMouseOverTop ||
  2259.             node.mIsMouseOverBottom ||
  2260.             node.mIsMouseOverLeft ||
  2261.             node.mIsMouseOverRight
  2262.             )) {
  2263.             return;
  2264.         }
  2265.  
  2266.         this.addButtonIsShown = true;
  2267.  
  2268.         this.hideAddButton();
  2269.  
  2270.         var box = node.contentAreaSizeObject;
  2271.         if (!box) return;
  2272.  
  2273.         var button = this.addButton;
  2274.         button.targetSubBrowser = node;
  2275.  
  2276.         this.setButtonSizeAndPosition(aEvent);
  2277.         this.showHideAddButton(true, aEvent);
  2278.  
  2279.         this.addButtonIsShown = true;
  2280.  
  2281.         this.stopDelayedHideAddButtonTimer();
  2282.         this.delayedHideAddButton();
  2283.     },
  2284.     
  2285.     delayedShowAddButton : function(aEvent) 
  2286.     {
  2287.         if (this.showAddButtonTimer) {
  2288.             this.showAddButtonTimer = null;
  2289.             window.clearTimeout(this.showAddButtonTimer);
  2290.         }
  2291.  
  2292.         var button = this.addButton;
  2293.         if (
  2294.             this.addButtonIsShown &&
  2295.             (
  2296.                 (button.buttonPos == 'top' && !aEvent.isTop) ||
  2297.                 (button.buttonPos == 'bottom' && !aEvent.isBottom) ||
  2298.                 (button.buttonPos == 'left' && !aEvent.isLeft) ||
  2299.                 (button.buttonPos == 'right' && !aEvent.isRight) ||
  2300.                 (button.targetSubBrowser != aEvent.targetSubBrowser)
  2301.             )
  2302.             ) {
  2303.             this.hideAddButton();
  2304.         }
  2305.  
  2306.         var justNow = false;
  2307.         if (aEvent.firedBy.indexOf('drag') == 0) {
  2308.             justNow = this.getDraggingSubBrowser();
  2309.         }
  2310.         else if (aEvent.modifierKeyPressed) {
  2311.             justNow = true;
  2312.         }
  2313.  
  2314.         this.showAddButtonTimer = window.setTimeout(
  2315.             this.delayedShowAddButtonCallback,
  2316.             (justNow ? 0 : this.addButtonShowDelay),
  2317.             this,
  2318.             aEvent
  2319.         );
  2320.     },
  2321.     
  2322.     delayedShowAddButtonCallback : function(aThis, aEvent) 
  2323.     {
  2324.         aThis.showAddButton(aEvent);
  2325.     },
  2326.    
  2327.     hideAddButton : function(aEvent) 
  2328.     {
  2329.         this.stopDelayedHideAddButtonTimer();
  2330.         if (!this.addButtonIsShown) return;
  2331.  
  2332.         var button = this.addButton;
  2333.         this.showHideAddButton(false);
  2334.         button.targetSubBrowser = null;
  2335.  
  2336.         if (aEvent && aEvent.force) {
  2337.             if (this.showAddButtonTimer) {
  2338.                 window.clearTimeout(this.showAddButtonTimer);
  2339.                 this.showAddButtonTimer = null;
  2340.             }
  2341.         }
  2342.  
  2343.         this.addButtonIsShown = false;
  2344.     },
  2345.     
  2346.     delayedHideAddButton : function() 
  2347.     {
  2348.         if (this.hideAddButtonTimer) return;
  2349.         this.hideAddButtonTimer = window.setTimeout(this.delayedHideAddButtonCallback, this.addButtonHideDelay, this);
  2350.     },
  2351.     
  2352.     delayedHideAddButtonCallback : function(aThis) 
  2353.     {
  2354.         aThis.stopDelayedHideAddButtonTimer();
  2355.         aThis.hideAddButton();
  2356.     },
  2357.  
  2358.     stopDelayedHideAddButtonTimer : function() 
  2359.     {
  2360.         window.clearTimeout(this.hideAddButtonTimer);
  2361.         this.hideAddButtonTimer = null;
  2362.     },
  2363.    
  2364.     showHideAddButton : function(aShow, aEvent) 
  2365.     {
  2366.         var button = this.addButton;
  2367.         if (button.animationTask)
  2368.             window['piro.sakura.ne.jp'].animationManager.removeTask(button.animationTask);
  2369.  
  2370.         this.addButtonIsActive = false;
  2371.  
  2372.         var self = this;
  2373.         var popup = button.parentNode;
  2374.         var doAnimation = function() {
  2375.                 button.animationTask = function(aTime, aBeginningValue, aTotalChange, aDuration) {
  2376.                     var opacity;
  2377.                     if (aTime >= aDuration) {
  2378.                         delete button.animationTask;
  2379.                         button.style.opacity = aShow ? 1 : 0 ;
  2380.                         if (!aShow) popup.hidePopup();
  2381.                         self.addButtonIsActive = aShow;
  2382.                         self.addButtonIsShown = aShow;
  2383.                         return true;
  2384.                     }
  2385.                     else {
  2386.                         opacity = aTime / aDuration;
  2387.                         if (!aShow) opacity = 1 - opacity;
  2388.                         button.style.opacity = opacity;
  2389.                         return false;
  2390.                     }
  2391.                 };
  2392.                 window['piro.sakura.ne.jp'].animationManager.addTask(
  2393.                     button.animationTask,
  2394.                     0, 0, self.addButtonFadeDuration
  2395.                 );
  2396.             };
  2397.  
  2398.         if (aShow) {
  2399.             let box  = aEvent.targetSubBrowser.contentAreaSizeObject;
  2400.             let size = this.addButtonSize;
  2401.             let x = 0,
  2402.                 y = 0,
  2403.                 w = size,
  2404.                 h = size,
  2405.                 pos;
  2406.             if (aEvent.isTop) {
  2407.                 pos = 'top';
  2408.                 w = box.areaWidth;
  2409.                 x = box.areaX;
  2410.                 y = box.y;
  2411.             }
  2412.             else if (aEvent.isBottom) {
  2413.                 pos = 'bottom';
  2414.                 w = box.areaWidth;
  2415.                 x = box.areaX;
  2416.                 y = box.y + box.height - size;
  2417.             }
  2418.             else if (aEvent.isLeft) {
  2419.                 pos = 'left';
  2420.                 h = box.areaHeight;
  2421.                 x = box.x;
  2422.                 y = box.areaY;
  2423.             }
  2424.             else if (aEvent.isRight) {
  2425.                 pos = 'right';
  2426.                 h = box.areaHeight;
  2427.                 x = box.x + box.width - size;
  2428.                 y = box.areaY;
  2429.             }
  2430.  
  2431.             button.style.opacity = 0;
  2432.             button.style.width = button.width = 0;
  2433.             button.style.height = button.height = 0;
  2434.  
  2435.             var showPopup = function() {
  2436.                     button.className = 'splitbrowser-add-button '+pos;
  2437.                     button.buttonPos = pos;
  2438.                     button.setAttribute('tooltiptext', button.getAttribute('tooltiptext-'+pos));
  2439.  
  2440.                     popup.style.border = '1px transparent solid'; // hack for correct rendering
  2441.  
  2442.                     popup.addEventListener('popupshown', function() {
  2443.                         popup.removeEventListener('popupshown', arguments.callee, false);
  2444.                         button.style.width = (button.width = w)+'px';
  2445.                         button.style.height = (button.height = h)+'px';
  2446.                         doAnimation();
  2447.                     }, false);
  2448.  
  2449.                     let rootBox = document.documentElement.boxObject;
  2450.                     // "-1" is for the transparent border
  2451.                     popup.openPopupAtScreen(
  2452.                         x + rootBox.screenX - 1,
  2453.                         y + rootBox.screenY - 1,
  2454.                         false
  2455.                     );
  2456.                 };
  2457.  
  2458.             window.setTimeout(function() {
  2459.                 if (popup.popupBoxObject.popupState == 'closed') {
  2460.                     showPopup();
  2461.                 }
  2462.                 else {
  2463.                     popup.addEventListener('popuphidden', function() {
  2464.                         popup.removeEventListener('popuphidden', arguments.callee, false);
  2465.                         showPopup();
  2466.                     }, false);
  2467.                     popup.hidePopup();
  2468.                 }
  2469.             }, 0);
  2470.         }
  2471.         else {
  2472.             if (popup.popupBoxObject.popupState != 'closed')
  2473.                 doAnimation();
  2474.         }
  2475.     },
  2476.  
  2477.     onAddButtonCommand : function(aEvent) 
  2478.     {
  2479.         var browser = aEvent.target.targetSubBrowser;
  2480.         this.fireSubBrowserAddRequestEventFromFrame(browser.contentWindow, null, this['POSITION_'+aEvent.target.buttonPos.toUpperCase()], aEvent.target);
  2481.         SplitBrowser.hideAddButton(aEvent, true);
  2482.         window.setTimeout('SplitBrowser.hideAddButton(null, true)', 0);
  2483.     },
  2484.  
  2485.     addButtonDNDObserver : { 
  2486.         onDragOver : function() {},
  2487.  
  2488.         onDrop: function(aEvent, aXferData, aDragSession)
  2489.         {
  2490.             aEvent.preventDefault();
  2491.             aEvent.preventBubble();
  2492.  
  2493.             var uri = SplitBrowser.getURIFromDragData(aXferData, aDragSession, aEvent);
  2494.             var tab = SplitBrowser.getTabFromChild(aDragSession.sourceNode);
  2495.             if (!uri && !tab) return;
  2496.  
  2497.             var tabbrowser = SplitBrowser.getTabBrowserFromChild(tab);
  2498.             if (!tabbrowser) tab = null;
  2499.  
  2500.             SplitBrowser.fireSubBrowserAddRequestEventFromButton(
  2501.                 tab || uri,
  2502.                 SplitBrowser.isAccelKeyPressed(aEvent)
  2503.             );
  2504.             window.setTimeout('SplitBrowser.hideAddButton();', 0);
  2505.         },
  2506.  
  2507.         getSupportedFlavours: function ()
  2508.         {
  2509.             var flavourSet = new FlavourSet();
  2510.             flavourSet.appendFlavour('application/x-moz-splitbrowser');
  2511.             flavourSet.appendFlavour('application/x-moz-tabbrowser-tab');
  2512.             flavourSet.appendFlavour('text/x-moz-url');
  2513.             flavourSet.appendFlavour('text/unicode');
  2514.             flavourSet.appendFlavour('application/x-moz-file', 'nsIFile');
  2515.             return flavourSet;
  2516.         }
  2517.     },
  2518.  
  2519. /* for Firefox versions */ 
  2520.     
  2521.     setButtonSizeAndPosition : function(aEvent) 
  2522.     {
  2523.     },
  2524.    
  2525. /* drag-and-drop */ 
  2526.     
  2527.     getDraggingSubBrowser : function() 
  2528.     {
  2529.         var session = this.getCurrentDragSession();
  2530.         if (!session) return false;
  2531.         var dragged = session.sourceNode;
  2532.         return this.getTabBrowserFromChild(dragged) ?
  2533.                 null :
  2534.                 this.getSubBrowserFromChild(dragged) ;
  2535.     },
  2536.  
  2537.     getDropPositionOnContentArea : function(aEvent, aBox) 
  2538.     {
  2539.         var W = Math.max(1, aBox.boxObject.width);
  2540.         var H = Math.max(1, aBox.boxObject.height);
  2541.         var X = aBox.boxObject.screenX;
  2542.         var Y = aBox.boxObject.screenY;
  2543.         var x = aEvent.screenX - X;
  2544.         var y = aEvent.screenY - Y;
  2545.  
  2546.         // ù╠êµé╠Æ[é⌐éτ30üôé╠òöò¬é╛é»ö╜ë₧üBé╗éΩéµéΦôαæñé═û│ÄïüB
  2547.         var Wunit = W * 0.3;
  2548.         var Hunit = H * 0.3;
  2549.         if (Wunit < x && W - Wunit > x &&
  2550.             Hunit < y && H - Hunit > y)
  2551.             return SplitBrowser.POSITION_INSIDE;
  2552.  
  2553.         var isTL = x <= W - (y * W / H);
  2554.         var isBL = x <= y * W / H;
  2555.  
  2556.         return (isTL && isBL) ? SplitBrowser.POSITION_LEFT :
  2557.             (isTL && !isBL) ? SplitBrowser.POSITION_TOP :
  2558.             (!isTL && isBL) ? SplitBrowser.POSITION_BOTTOM :
  2559.             SplitBrowser.POSITION_RIGHT ;
  2560.     },
  2561.  
  2562.     getURIFromDragData : function(aXferData, aDragSession, aEvent) 
  2563.     {
  2564.         var uri = null;
  2565.  
  2566.         try{
  2567.             if (aXferData.flavour.contentType == 'application/x-moz-splitbrowser') {
  2568.                 uri = aXferData.data;
  2569.                 if (this.isAccelKeyPressed(aEvent)) {
  2570.                     try {
  2571.                         var info = this.evalInSandbox('('+uri.replace('subbrowser\n', '')+')');
  2572.                         info.clone = true;
  2573.                         uri = 'subbrowser\n'+info.toSource();
  2574.                     }
  2575.                     catch(e) {
  2576.                     }
  2577.                 }
  2578.             }
  2579.             else if (aXferData.flavour.contentType == 'application/x-moz-tabbrowser-tab') {
  2580.                 uri = aXferData.data.linkedBrowser.currentURI;
  2581.                 uri = uri ? uri.spec : 'about:blank' ;
  2582.             }
  2583.             else {
  2584.                 // "window.retrieveURLFromData()" is old implementation
  2585.                 uri = 'retrieveURLFromData' in window ? retrieveURLFromData(aXferData.data, aXferData.flavour.contentType) : transferUtils.retrieveURLFromData(aXferData.data, aXferData.flavour.contentType) ;
  2586.  
  2587.                 if (!uri || !uri.length || uri.indexOf(' ', 0) != -1)
  2588.                     return null;
  2589.  
  2590.                 var sourceDoc = aDragSession.sourceDocument;
  2591.                 if (sourceDoc &&
  2592.                     sourceDoc.documentURI.indexOf('chrome://') < 0) {
  2593.                     var sourceURI = sourceDoc.documentURI;
  2594.                     const nsIScriptSecurityManager = Components.interfaces.nsIScriptSecurityManager;
  2595.                     var secMan = Components.classes['@mozilla.org/scriptsecuritymanager;1'].getService(nsIScriptSecurityManager);
  2596.                     try {
  2597.                         secMan.checkLoadURIStr(sourceURI, uri, nsIScriptSecurityManager.STANDARD);
  2598.                     }
  2599.                     catch(e) {
  2600.                         aEvent.stopPropagation();
  2601.                         throw sourceURI+'\nDrop of ' + uri + ' denied.';
  2602.                     }
  2603.                 }
  2604.  
  2605.                 uri = getShortcutOrURI(uri);
  2606.             }
  2607.         }
  2608.         catch(e) {
  2609.             alert(e);
  2610.         }
  2611.  
  2612.         return uri;
  2613.     },
  2614.  
  2615.     getURIFromDragEvent : function(aEvent, aDragSession) 
  2616.     {
  2617.         var uri = null;
  2618.  
  2619.         try {
  2620.             var dt = aEvent.dataTransfer;
  2621.             var types = dt.types;
  2622.             if (types.contains('application/x-moz-splitbrowser')) {
  2623.                 uri = dt.getData('application/x-moz-splitbrowser');
  2624.                 if (this.isAccelKeyPressed(aEvent)) {
  2625.                     try {
  2626.                         var info = this.evalInSandbox('('+uri.replace('subbrowser\n', '')+')');
  2627.                         info.clone = true;
  2628.                         uri = 'subbrowser\n'+info.toSource();
  2629.                     }
  2630.                     catch(e) {
  2631.                     }
  2632.                 }
  2633.             }
  2634.             else if (types.contains('application/x-moz-tabbrowser-tab')) {
  2635.                 uri = dt.getData('application/x-moz-tabbrowser-tab').linkedBrowser.currentURI;
  2636.                 uri = uri ? uri.spec : 'about:blank' ;
  2637.             }
  2638.             else {
  2639.                 uri = browserDragAndDrop.getDragURLFromDataTransfer(dt);
  2640.  
  2641.                 if (!uri || !uri.length || uri.indexOf(' ', 0) != -1)
  2642.                     return null;
  2643.  
  2644.                 var sourceDoc = aDragSession.sourceDocument;
  2645.                 if (sourceDoc &&
  2646.                     sourceDoc.documentURI.indexOf('chrome://') < 0) {
  2647.                     var sourceURI = sourceDoc.documentURI;
  2648.                     const nsIScriptSecurityManager = Components.interfaces.nsIScriptSecurityManager;
  2649.                     var secMan = Components.classes['@mozilla.org/scriptsecuritymanager;1'].getService(nsIScriptSecurityManager);
  2650.                     try {
  2651.                         secMan.checkLoadURIStr(sourceURI, uri, nsIScriptSecurityManager.STANDARD);
  2652.                     }
  2653.                     catch(e) {
  2654.                         aEvent.stopPropagation();
  2655.                         throw sourceURI+'\nDrop of ' + uri + ' denied.';
  2656.                     }
  2657.                 }
  2658.  
  2659.                 uri = getShortcutOrURI(uri);
  2660.             }
  2661.         }
  2662.         catch(e) {
  2663.             alert(e);
  2664.         }
  2665.         return uri;
  2666.     },
  2667.  
  2668.     fireSubBrowserAddRequestEventFromButton : function(aTabOrURI, aDuplicate) 
  2669.     {
  2670.         var button = this.addButton;
  2671.         var browser = button.targetSubBrowser || this.mainBrowserBox;
  2672.         var position = SplitBrowser['POSITION_'+button.buttonPos.toUpperCase()];
  2673.         if (typeof aTabOrURI == 'string') {
  2674.             this.fireSubBrowserAddRequestEvent(
  2675.                 aTabOrURI,
  2676.                 browser,
  2677.                 position,
  2678.                 button
  2679.             );
  2680.         }
  2681.         else {
  2682.             this.fireSubBrowserAddRequestEventFromFrame(
  2683.                 aTabOrURI.linkedBrowser.contentWindow,
  2684.                 browser,
  2685.                 position,
  2686.                 button,
  2687.                 aDuplicate
  2688.             );
  2689.         }
  2690.     },
  2691.  
  2692.     performDropOnTabBrowser : function(aArgs, aTabBrowser) 
  2693.     {
  2694.         if (!aTabBrowser || aTabBrowser.localName != 'tabbrowser') return false;
  2695.  
  2696.         aArgs = Array.slice(aArgs);
  2697.         if (!aArgs.length) return false;
  2698.         var event = aArgs[0];
  2699.  
  2700.         var dragSession;
  2701.         if (aArgs.length == 1) { // Firefox 3.1 or later
  2702.             dragSession = this.getCurrentDragSession();
  2703.         }
  2704.         else { // Firefox 3.0.x
  2705.             dragSession = aArgs[2];
  2706.         }
  2707.         if (!dragSession) return false;
  2708.  
  2709.         var isCopy = this.isAccelKeyPressed(event);
  2710.  
  2711.         var oldTab = this.getTabFromChild(dragSession.sourceNode);
  2712.         if (oldTab && 'treeStyleTab' in aTabBrowser) return false;
  2713.  
  2714.         var oldTabBrowser = this.getTabBrowserFromChild(oldTab);
  2715.         if (oldTab) {
  2716.             if (oldTabBrowser == aTabBrowser) {
  2717.                 return false;
  2718.             }
  2719.             else {
  2720.                 var oldTabs = this.getDraggedTabs(oldTab);
  2721.                 var isCloseAll = !isCopy && (this.getTabs(oldTabBrowser).snapshotLength == oldTabs.length);
  2722.                 var newTabs = [];
  2723.                 oldTabs.forEach(function(aTab) {
  2724.                     var t = aTabBrowser.addTab();
  2725.                     newTabs.push(t);
  2726.                     if (isCopy)
  2727.                         this.cloneBrowser(aTab.linkedBrowser, t.linkedBrowser);
  2728.                     else
  2729.                         this.swapBrowser(aTab.linkedBrowser, t.linkedBrowser);
  2730.                 }, this);
  2731.                 aTabBrowser.selectedTab = newTabs[0];
  2732.                 this.selectNewTabsAfterDrop(newTabs, oldTabBrowser);
  2733.                 if (!isCopy) {
  2734.                     this.closeOldTabsAfterDrop(oldTabs, oldTabBrowser, isCloseAll);
  2735.                 }
  2736.                 return true;
  2737.             }
  2738.         }
  2739.  
  2740.         var draggedSubBrowser = this.getSubBrowserFromChild(dragSession.sourceNode);
  2741.         var droppedSubBrowser = this.getSubBrowserFromChild(aTabBrowser);
  2742.         if (draggedSubBrowser && draggedSubBrowser != droppedSubBrowser) {
  2743.             var tabs = this.addTabsFromSubBrowserInto(draggedSubBrowser, aTabBrowser, !isCopy);
  2744.             if (tabs.length < 1) return false;
  2745.             aTabBrowser.selectedTab = tabs[0];
  2746.             this.selectNewTabsAfterDrop(tabs, draggedSubBrowser);
  2747.             if (!isCopy) {
  2748.                 window.setTimeout(function() {
  2749.                     draggedSubBrowser.close(true);
  2750.                 }, 0);
  2751.             }
  2752.             return true;
  2753.         }
  2754.  
  2755.         // self-drop: do nothing!
  2756.         if (
  2757.             !isCopy &&
  2758.             !oldTab &&
  2759.             draggedSubBrowser &&
  2760.             draggedSubBrowser == droppedSubBrowser
  2761.             ) {
  2762.             return true;
  2763.         }
  2764.  
  2765.         return false;
  2766.     },
  2767.  
  2768.     performDropOnContentArea : function(aArgs) 
  2769.     {
  2770.         var event = aArgs[0];
  2771.         var xferData = aArgs[1];
  2772.         var dragSession = aArgs[2];
  2773.  
  2774.         var uri;
  2775.         if (xferData) { // old method
  2776.             uri = this.getURIFromDragData(xferData, dragSession, event);
  2777.         }
  2778.         else {
  2779.             dragSession = this.getCurrentDragSession();
  2780.             uri = this.getURIFromDragEvent(event, dragSession);
  2781.         }
  2782.         if (!uri) return true;
  2783.  
  2784.         // fallback for Linux
  2785.         // in Linux, "dragdrop" event doesn't fire on the button.
  2786.  
  2787.         var box = this.mainBrowserBox;
  2788.  
  2789.         var forceCheck = (
  2790.                 event.ctrlKey ||
  2791.                 (!xferData && event.dataTransfer.types.contains('application/x-moz-splitbrowser')) ||
  2792.                 (xferData && xferData.flavour.contentType == 'application/x-moz-splitbrowser')
  2793.             );
  2794.         var check = box.checkEventFiredOnEdge(event);
  2795.         if (!check) return true;
  2796.  
  2797.         if (this.isEventFiredOnTabbar(event, gBrowser)) {
  2798.             return;
  2799.         }
  2800.  
  2801.         if (
  2802.             (forceCheck || this.isLinux) &&
  2803.             this.addButton.targetSubBrowser == box &&
  2804.             (
  2805.                 check.inTopArea ||
  2806.                 check.inBottomArea ||
  2807.                 check.inLeftArea ||
  2808.                 check.inRightArea
  2809.             )
  2810.             ) {
  2811.             this.fireSubBrowserAddRequestEventFromButton(uri, this.isAccelKeyPressed(event));
  2812.             event.preventDefault();
  2813.             event.preventBubble();
  2814.             return true;
  2815.         }
  2816.  
  2817.         return false;
  2818.     },
  2819.  
  2820.     getDraggedTabs : function(aNode) 
  2821.     {
  2822.         var single = [aNode];
  2823.         var b = this.getTabBrowserFromChild(aNode);
  2824.         if (!b) return single;
  2825.  
  2826.         var tabs;
  2827.  
  2828.         if ('MultipleTabService' in window &&
  2829.             'getSelectedTabs' in MultipleTabService) {
  2830.             tabs = MultipleTabService.getSelectedTabs(b);
  2831.             if (tabs.length) return tabs;
  2832.         }
  2833.  
  2834.         if ('TreeStyleTabService' in window) {
  2835.             tabs = TreeStyleTabService.getDescendantTabs(aNode);
  2836.             if (tabs.length) return single.concat(tabs);
  2837.         }
  2838.  
  2839.         return single;
  2840.     },
  2841.  
  2842.     selectNewTabsAfterDrop : function(aTabs, aAnotherTabBrowser) 
  2843.     {
  2844.         if (
  2845.             !('MultipleTabService' in window) ||
  2846.             !('clearSelection' in MultipleTabService) ||
  2847.             !('setSelection' in MultipleTabService)
  2848.             )
  2849.             return;
  2850.  
  2851.         if (aAnotherTabBrowser) MultipleTabService.clearSelection(aAnotherTabBrowser);
  2852.         if (aTabs.length < 2) return;
  2853.         MultipleTabService.clearSelection(this.getTabBrowserFromChild(aTabs[0]));
  2854.         aTabs.forEach(function(aTab) {
  2855.             MultipleTabService.setSelection(aTab, true);
  2856.         });
  2857.     },
  2858.  
  2859.     closeOldTabsAfterDrop : function(aTabs, aTabBrowser, aIsCloseAll) 
  2860.     {
  2861.         // exclude already closed tabs
  2862.         aTabs = aTabs.filter(function(aTab) {
  2863.             return aTab.parentNode;
  2864.         });
  2865.         var b = aTabBrowser;
  2866.         if (aTabs.length) {
  2867.             b = b || this.getTabBrowserFromChild(aTabs[0]);
  2868.             aIsCloseAll = aIsCloseAll || (this.getTabs(b).snapshotLength == aTabs.length);
  2869.             if ('MultipleTabService' in window &&
  2870.                 'closeTabs' in MultipleTabService) {
  2871.                 MultipleTabService.closeTabs(aTabs);
  2872.             }
  2873.             else {
  2874.                 aTabs.reverse().forEach(function(aTab) {
  2875.                     b.removeTab(aTab);
  2876.                 }, this);
  2877.             }
  2878.         }
  2879.         if (aIsCloseAll && b) {
  2880.             b = this.getSubBrowserFromChild(b);
  2881.             if (b) b.close(true);
  2882.         }
  2883.     },
  2884.   
  2885.     /* splitter context menu */ 
  2886.     
  2887.     getSplitterTarget : function(aSplitter) 
  2888.     {
  2889.         var node = aSplitter.previousSibling;
  2890.         if (node.getAttribute('splitter') == 'after')
  2891.             return node;
  2892.         else
  2893.             return aSplitter.nextSibling;
  2894.     },
  2895.  
  2896.     updateSplitterSideBoxes : function(aEvent, aProp) 
  2897.     {
  2898.         var splitter = aEvent.originalTarget || aEvent.target;
  2899.         this.updateSplitterSideBox(splitter, splitter.previousSibling, aEvent, aProp);
  2900.         this.updateSplitterSideBox(splitter, splitter.nextSibling, aEvent, aProp);
  2901.     },
  2902.     updateSplitterSideBox : function(aSplitter, aNode, aEvent, aProp)
  2903.     {
  2904.         if (aEvent.type == 'mousedown') {
  2905.             aNode.splitterDragging = true;
  2906.             if (aNode['last'+aProp] === void(0) || aNode['last'+aProp] < 0) {
  2907.                 aNode['tempLast'+aProp] = aNode.boxObject[aProp];
  2908.             }
  2909.             else {
  2910.                 aNode['tempLast'+aProp] = -1;
  2911.             }
  2912.             aNode.removeAttribute('max'+aProp);
  2913.         }
  2914.         else {
  2915.             var cCProp = aProp == 'width' ? 'hContentCompletelyCollapsed' : 'vContentCompletelyCollapsed' ;
  2916.             if (aNode[cCProp] &&
  2917.                 aNode['tempLast'+aProp] !== void(0) &&
  2918.                 aNode['tempLast'+aProp] > -1 &&
  2919.                 (
  2920.                     aNode['last'+aProp] === void(0) ||
  2921.                     aNode['last'+aProp] < 0
  2922.                 )
  2923.                 ) {
  2924.                 aNode['last'+aProp] = aNode['tempLast'+aProp];
  2925.                 aNode.setAttribute('max'+aProp, 0);
  2926.             }
  2927.             aNode['tempLast'+aProp] = -1;
  2928.             window.setTimeout(function() {
  2929.                 aNode.splitterDragging = false;
  2930.             }, 0);
  2931.         }
  2932.  
  2933.         for (var i = 0, maxi = aNode.childNodes.length; i < maxi; i = i+2)
  2934.         {
  2935.             this.updateSplitterSideBox(aSplitter, aNode.childNodes[i], aEvent, aProp);
  2936.         }
  2937.     },
  2938.  
  2939.     updateSplitterContextMenu : function() 
  2940.     {
  2941.         var c = this.getSplitterTarget(document.popupNode);
  2942.         if (!c) return;
  2943.  
  2944.         var popup = document.getElementById('subbrowser-splitter-contextmenu');
  2945.         var collapsed = c.isCollapsed();
  2946.         popup.getElementsByAttribute('class', 'subbrowser-context-collapse')[0].hidden = collapsed;
  2947.         popup.getElementsByAttribute('class', 'subbrowser-context-expand')[0].hidden = !collapsed;
  2948.     },
  2949.  
  2950.     toggleSplitterCollapsed : function() 
  2951.     {
  2952.         var c = this.getSplitterTarget(document.popupNode);
  2953.         if (c) c.toggleCollapsed();
  2954.     },
  2955.   
  2956.     /* Web Search in split browser */ 
  2957.     
  2958.     initSearchBar : function() 
  2959.     {
  2960.         var search = this.searchbar;
  2961.         if (!search || search.splitbrowserInitialized) return;
  2962.  
  2963.         var textbox = this.textbox;
  2964.  
  2965.         if ('handleSearchCommand' in search) { // Firefox 2 or later
  2966.             var funcs = 'doSearch __secondsearch__doSearch'.split(' ');
  2967.             var source;
  2968.             for (var i in funcs)
  2969.             {
  2970.                 source = search[funcs[i]].toSource();
  2971.                 if (/^\(?function doSearch\(/.test(source)) {
  2972.                     if (source.indexOf('openUILinkIn') > -1) { // Firefox 3
  2973.                         eval('search.'+funcs[i]+' = '+source.replace(
  2974.                             '{',
  2975.                             '$& SplitBrowser.readyToOpenSpecialPane("search");'
  2976.                         ).replace(
  2977.                             /(\}\)?)$/,
  2978.                             'SplitBrowser.specialPaneOpened("search"); $1'
  2979.                         ));
  2980.                     }
  2981.                     else { // Firefox 2
  2982.                         eval('search.'+funcs[i]+' = '+source.replace(
  2983.                             /(getBrowser\(\)|gBrowser)/g,
  2984.                             'SplitBrowser.browserForSearch'
  2985.                         ).replace(
  2986.                             /content.focus\(\)/g,
  2987.                             'SplitBrowser.browserForSearch.contentWindow.focus()'
  2988.                         ).replace(
  2989.                             /([^.])loadURI\(([^\),]+), ([^\),]+), ([^\),]+), ([^\),]+)\)/,
  2990.                             '$1SplitBrowser.browserForSearch.webNavigation.loadURI($2, Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE, $3, $4, null)'
  2991.                         ));
  2992.                     }
  2993.                     break;
  2994.                 }
  2995.             }
  2996.         }
  2997.         else if ('onEnginePopupCommand' in textbox && textbox.onEnginePopupCommand.toSource().indexOf('SplitBrowser') < 0) { // Firefox 1.5
  2998.             eval('textbox.onEnginePopupCommand = '+textbox.onEnginePopupCommand.toSource().replace(
  2999.                 /([^.])loadURI\(/,
  3000.                 '$1SplitBrowser.browserForSearch.loadURI('
  3001.             ));
  3002.         }
  3003.  
  3004.         search.splitbrowserInitialized = true;
  3005.     },
  3006.  
  3007.     get browserForSearch() 
  3008.     {
  3009.         if (!this.tabbedBrowsingEnabled) return gBrowser;
  3010.  
  3011.         var b;
  3012.         switch (this.getPref('splitbrowser.search.loadResultsIn'))
  3013.         {
  3014.             default:
  3015.             case 0:
  3016.                 b = gBrowser ; // document.getElementById('content') ;
  3017.                 break;
  3018.             case 1:
  3019.                 b = this.activeBrowser;
  3020.                 break;
  3021.             case 2:
  3022.                 b = this.getSubBrowserByName('search');
  3023.                 if (!b) {
  3024.                     b = this.addSubBrowser('about:blank', null, this.POSITION_RIGHT, 'search');
  3025.                 }
  3026.                 b = b.browser;
  3027.                 break;
  3028.         }
  3029.         return b;
  3030.     },
  3031.  
  3032.     get searchbar() 
  3033.     {
  3034.         var bar = document.getElementsByTagName('searchbar');
  3035.         return bar && bar.length ? bar[0] : null ;
  3036.     },
  3037.  
  3038.     get textbox() 
  3039.     {
  3040.         var bar = this.searchbar;
  3041.         return bar ? (
  3042.                 bar._textbox || /* Firefox 2 */
  3043.                 bar.mTextbox /* Firefox 1.5 */
  3044.             ) : null ;
  3045.     },
  3046.   
  3047.     /* special panes */ 
  3048.     specialPane : null,
  3049.     
  3050.     readyToOpenSpecialPane : function(aType) 
  3051.     {
  3052.         this.specialPane = aType;
  3053.     },
  3054.  
  3055.     specialPaneOpened : function(aType) 
  3056.     {
  3057.         this.specialPane = null;
  3058.     },
  3059.  
  3060.     checkToOpenSpecialPane : function(aURI, aWhere, aAllowThirdPartyFixup, aPostData, aReferrerURI) 
  3061.     {
  3062.         switch (this.specialPane)
  3063.         {
  3064.             case 'search':
  3065.                 var b = this.browserForSearch;
  3066.                 if (b == gBrowser) return false;
  3067.  
  3068.                 var loadInBackground = this.getPref('browser.tabs.loadInBackground');
  3069.                 switch (aWhere)
  3070.                 {
  3071.                     default:
  3072.                         b.webNavigation.loadURI(
  3073.                             aURI,
  3074.                             (aAllowThirdPartyFixup ?
  3075.                                 Components.interfaces.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP :
  3076.                                 Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE
  3077.                             ),
  3078.                             aReferrerURI,
  3079.                             aPostData,
  3080.                             null
  3081.                         );
  3082.                         break;
  3083.  
  3084.                     case 'tabshifted':
  3085.                         loadInBackground = !loadInBackground;
  3086.                     case 'tab':
  3087.                         b.loadOneTab(
  3088.                             aURI,
  3089.                             aReferrerURI,
  3090.                             null,
  3091.                             aPostData,
  3092.                             loadInBackground,
  3093.                             aAllowThirdPartyFixup || false
  3094.                         );
  3095.                         break;
  3096.                 }
  3097.                 b.contentWindow.focus();
  3098.                 return true;
  3099.  
  3100.             default:
  3101.                 return false;
  3102.         }
  3103.     },
  3104.   
  3105. /* Find Bar */ 
  3106.     
  3107.     updateFindBar : function(aEvent) 
  3108.     {
  3109.         var bar = document.getElementById('FindToolbar');
  3110.         if (!bar) return;
  3111.  
  3112.         var old = aEvent.lastFocused;
  3113.         var oldB;
  3114.         try {
  3115.             oldB = old ? (old.browser ? old.browser : gBrowser ) : null ;
  3116.             if (oldB) {
  3117.                 oldB.fastFind.setSelectionModeAndRepaint(Components.interfaces.nsISelectionController.SELECTION_ON);
  3118.                 bar._highlightDoc(null, null, oldB.contentWindow);
  3119.             }
  3120.         }
  3121.         catch(e) {
  3122.         }
  3123.  
  3124.         var b = this.activeBrowser;
  3125.         bar._findField.value = b.findString;
  3126.         bar.setAttribute('browserid', b.getAttribute('id'));
  3127.         bar.browser = b;
  3128.  
  3129. /*
  3130.         var check = document.getElementById('highlight');
  3131.         if (check && check.checked) {
  3132.             gFindBar.toggleHighlight(true);
  3133.         }
  3134. */
  3135.  
  3136.         var check = bar.getElement("match-case-status");
  3137.         if (check)
  3138.             b.fastFind.caseSensitive = check.checked;
  3139.     },
  3140.   
  3141. /* Text Zoom */ 
  3142.     
  3143.     overrideZoomManager : function() 
  3144.     {
  3145.         window.getMarkupDocumentViewer = function() {
  3146.             return SplitBrowser.activeBrowser.markupDocumentViewer;
  3147.         };
  3148.  
  3149.         [
  3150.             'onContentPrefSet',
  3151.             'onContentPrefRemoved',
  3152.             'setSettingValue',
  3153.             '_applyPrefToSetting',
  3154.             '_applySettingToPref',
  3155.             '_removePref'
  3156.         ].forEach(function(aFunc) {
  3157.             if ('FullZoom' in window && aFunc in FullZoom) {
  3158.                 eval('FullZoom.'+aFunc+' = '+FullZoom[aFunc].toSource().replace(
  3159.                     /gBrowser/g,
  3160.                     'SplitBrowser.activeBrowser'
  3161.                 ));
  3162.             }
  3163.         }, this);
  3164.  
  3165.         if ('ZoomManager' in window && 'zoom' in ZoomManager) {
  3166.             try {
  3167.                 var zoomGetter = ZoomManager.__lookupGetter__('zoom');
  3168.                 var zoomSetter = ZoomManager.__lookupSetter__('zoom');
  3169.                 eval('zoomGetter = '+zoomGetter.toSource().replace(
  3170.                     /getBrowser\(\)/g,
  3171.                     'SplitBrowser.activeBrowser'
  3172.                 ));
  3173.                 eval('zoomSetter = '+zoomSetter.toSource().replace(
  3174.                     /getBrowser\(\)/g,
  3175.                     'SplitBrowser.activeBrowser'
  3176.                 ));
  3177.                 ZoomManager.__defineGetter__('zoom', zoomGetter);
  3178.                 ZoomManager.__defineSetter__('zoom', zoomSetter);
  3179.             }
  3180.             catch(e) {
  3181.             }
  3182.         }
  3183.     },
  3184.   
  3185. /* CtrlTab (Tab Previews) */ 
  3186.     
  3187.     overrideCtrlTab : function() 
  3188.     {
  3189.         if (!('ctrlTab' in window)) return;
  3190.  
  3191.         if ('tabList' in ctrlTab) {
  3192.             var tabListGetter = ctrlTab.__lookupGetter__('tabList');
  3193.             eval('tabListGetter = '+tabListGetter.toSource().replace(
  3194.                 'if (!this._useTabBarOrder && this.recentlyUsedLimit != 0) {',
  3195.                 <![CDATA[
  3196.                     SplitBrowser.browsers.forEach(function(aSubBrowser) {
  3197.                         var b = aSubBrowser.browser;
  3198.                         if (b.localName != 'tabbrowser') return;
  3199.                         list = list.concat(Array.slice(b.mTabs));
  3200.                     });
  3201.                 $&]]>.toString()
  3202.             ));
  3203.             ctrlTab.__defineGetter__('tabList', tabListGetter);
  3204.         }
  3205.         if ('onPopupHiding' in ctrlTab) {
  3206.             eval('ctrlTab.onPopupHiding = '+ctrlTab.onPopupHiding.toSource().replace(
  3207.                 /gBrowser(\.selectedTab = this\._tabToSelect)/,
  3208.                 <![CDATA[
  3209.                     SplitBrowser.getTabBrowserFromChild(this._tabToSelect)$1;
  3210.                     (function(aTab) {
  3211.                         var subbrowser = SplitBrowser.getSubBrowserFromChild(aTab);
  3212.                         if (subbrowser) subbrowser.focus();
  3213.                     })(this._tabToSelect);
  3214.                 ]]>.toString()
  3215.             ));
  3216.         }
  3217.         if ('updatePreview' in ctrlTab) {
  3218.             eval('ctrlTab.updatePreview = '+ctrlTab.updatePreview.toSource().replace(
  3219.                 /aTab\.label/g,
  3220.                 <![CDATA[(function(aTab) {
  3221.                     var tabbrowser = SplitBrowser.getTabBrowserFromChild(aTab);
  3222.                     var subbrowser = SplitBrowser.getSubBrowserFromChild(aTab);
  3223.                     return (subbrowser && tabbrowser.mTabs.length == 1) ?
  3224.                         subbrowser.title.getAttribute('value') :
  3225.                         aTab.label ;
  3226.                 })(aTab)]]>.toString()
  3227.             ));
  3228.         }
  3229.     },
  3230.   
  3231.     init : function() 
  3232.     {
  3233.         this.undoCache.registerBroadcaster(this.undoBroadcaster);
  3234.         this.ObserverService.addObserver(this, 'private-browsing', false);
  3235.  
  3236.         document.documentElement.addEventListener('SubBrowserAddRequest', this, true);
  3237.         document.documentElement.addEventListener('SubBrowserAddRequestFromContent', this, true, true);
  3238.         document.documentElement.addEventListener('SubBrowserRemoveRequest', this, true);
  3239.         document.documentElement.addEventListener('SubBrowserRemoveRequestFromContent', this, true, true);
  3240.         document.documentElement.addEventListener('SubBrowserAdded', this, true);
  3241.         document.documentElement.addEventListener('SubBrowserRemoved', this, true);
  3242.         document.documentElement.addEventListener('SubBrowserContentCollapsed', this, true);
  3243.         document.documentElement.addEventListener('SubBrowserContentExpanded', this, true);
  3244.         document.documentElement.addEventListener('SubBrowserEnterContentAreaEdge', this, true);
  3245.         document.documentElement.addEventListener('SubBrowserHoverContentAreaEdge', this, true);
  3246.         document.documentElement.addEventListener('SubBrowserExitContentAreaEdge', this, true);
  3247.         document.documentElement.addEventListener('SubBrowserFocusMoved', this, true);
  3248.         document.documentElement.addEventListener('TabOpen', this, true);
  3249.         document.documentElement.addEventListener('TabClose', this, true);
  3250.         document.documentElement.addEventListener('keydown', this, true);
  3251.         document.documentElement.addEventListener('keyup', this, true);
  3252.  
  3253.         document.getElementById('contentAreaContextMenu').addEventListener('popupshowing', this, false);
  3254.  
  3255.         window.addEventListener('resize', this, false);
  3256.         window.addEventListener('fullscreen', this, false);
  3257.         window.addEventListener('unload', this, false);
  3258.  
  3259.         window.removeEventListener('load', this, false);
  3260.  
  3261.         this.updateTabBrowser(gBrowser);
  3262.  
  3263.         gBrowser.__splitbrowser__updateCurrentBrowser = gBrowser.updateCurrentBrowser;
  3264.         gBrowser.updateCurrentBrowser = this.newUpdateCurrentBrowser;
  3265.  
  3266.         if ('contentAreaDNDObserver' in window) {
  3267.             eval('contentAreaDNDObserver.onDrop = '+contentAreaDNDObserver.onDrop.toSource().replace(
  3268.                 '{',
  3269.                 '{ if (SplitBrowser.performDropOnContentArea(arguments)) return;'
  3270.             ));
  3271.             if ('getSupportedFlavours' in contentAreaDNDObserver) {
  3272.                 eval('contentAreaDNDObserver.getSupportedFlavours = '+contentAreaDNDObserver.getSupportedFlavours.toSource().replace(
  3273.                     'flavourSet.appendFlavour(',
  3274.                     'flavourSet.appendFlavour("application/x-moz-splitbrowser"); flavourSet.appendFlavour("application/x-moz-tabbrowser-tab"); $&'
  3275.                 ));
  3276.             }
  3277.         }
  3278.  
  3279.         [
  3280.             'initMenu',
  3281.             '__textlink__initItems'
  3282.         ].forEach(function(aFunc) {
  3283.             if (!(aFunc in nsContextMenu.prototype)) return;
  3284.             eval('nsContextMenu.prototype.'+aFunc+' = '+nsContextMenu.prototype[aFunc].toSource().replace(
  3285.                 /this\.browser = aBrowser/g,
  3286.                 'this.browser = (aBrowser == gBrowser ? SplitBrowser.activeBrowser : aBrowser )'
  3287.             ));
  3288.         }, this);
  3289.  
  3290.         eval('window.nsBrowserAccess.prototype.openURI = '+window.nsBrowserAccess.prototype.openURI.toSource().replace(
  3291.             /switch\s*\(aWhere\)/,
  3292.             <![CDATA[
  3293.                 var pos = aOpener &&
  3294.                         aOpener.document &&
  3295.                         aOpener.document.documentElement &&
  3296.                         (pos = aOpener.document.documentElement.getAttribute('_moz-split-browser-to')) &&
  3297.                         /^(top|right|bottom|left|tab)$/i.test(pos) ? pos.toUpperCase() : null ;
  3298.                 if (pos && pos == 'TAB') {
  3299.                     aWhere = Components.interfaces.nsIBrowserDOMWindow.OPEN_NEWTAB;
  3300.                 }
  3301.                 else if (pos) {
  3302.                     pos = SplitBrowser['POSITION_'+pos];
  3303.                     var target = null;
  3304.                     var browsers = SplitBrowser.getSubBrowserAndBrowserFromFrame(aOpener);
  3305.                     if (browsers.subBrowser)
  3306.                         target = browsers.subBrowser;
  3307.  
  3308.                     var referrer = Components.classes['@mozilla.org/network/io-service;1']
  3309.                             .getService(Components.interfaces.nsIIOService)
  3310.                             .newURI(aOpener.location, null, null);
  3311.  
  3312.                     url = url.replace(RegExp.$1, '');
  3313.                     var subbrowser = SplitBrowser.addSubBrowser(null, target, pos);
  3314.                     var win = subbrowser.browser.docShell
  3315.                                 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  3316.                                 .getInterface(Components.interfaces.nsIDOMWindow);
  3317.                     try {
  3318.                         win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  3319.                             .getInterface(Components.interfaces.nsIWebNavigation)
  3320.                             .loadURI(url, loadflags, referrer, null, null);
  3321.                     }
  3322.                     catch(e) {
  3323.                     }
  3324.  
  3325.                     return win;
  3326.                 };
  3327.  
  3328.                 switch(aWhere)
  3329.             ]]>
  3330.         ));
  3331.  
  3332.         eval('window.nsBrowserAccess.prototype.isTabContentWindow = '+window.nsBrowserAccess.prototype.isTabContentWindow.toSource().replace(
  3333.             '{',
  3334.             <![CDATA[$&
  3335.                 return SplitBrowser.getSubBrowserAndBrowserFromFrame(aWindow).browser ? true : false ;
  3336.             ]]>
  3337.         ));
  3338.  
  3339.         if (this.tabbedBrowsingEnabled) {
  3340.             eval('window.nsBrowserAccess.prototype.openURI = '+window.nsBrowserAccess.prototype.openURI.toSource().replace(
  3341.                 /gBrowser/g,
  3342.                 'SplitBrowser.activeBrowser'
  3343.             ));
  3344.         }
  3345.  
  3346.         window.QueryInterface(Components.interfaces.nsIDOMChromeWindow).browserDOMWindow = null;
  3347.         window.QueryInterface(Components.interfaces.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess();
  3348.  
  3349.  
  3350.         this.initSearchBar();
  3351.         var toolbox = document.getElementById('navigator-toolbox');
  3352.         if (toolbox.customizeDone) {
  3353.             toolbox.__splitbrowser__customizeDone = toolbox.customizeDone;
  3354.             toolbox.customizeDone = function(aChanged) {
  3355.                 this.__splitbrowser__customizeDone(aChanged);
  3356.                 SplitBrowser.initSearchBar();
  3357.             };
  3358.         }
  3359.         if ('BrowserToolboxCustomizeDone' in window) {
  3360.             window.__splitbrowser__BrowserToolboxCustomizeDone = window.BrowserToolboxCustomizeDone;
  3361.             window.BrowserToolboxCustomizeDone = function(aChanged) {
  3362.                 window.__splitbrowser__BrowserToolboxCustomizeDone.apply(window, arguments);
  3363.                 SplitBrowser.initSearchBar();
  3364.             };
  3365.         }
  3366.  
  3367.         if ('SearchLoadURL' in window) {
  3368.             eval('window.SearchLoadURL = '+window.SearchLoadURL.toSource().replace(
  3369.                 /(getBrowser\(\)|gBrowser)/g,
  3370.                 'SplitBrowser.browserForSearch'
  3371.             ).replace(
  3372.                 /content.focus\(\)/g,
  3373.                 'SplitBrowser.browserForSearch.contentWindow.focus()'
  3374.             ).replace(
  3375.                 /([^.])loadURI\(/,
  3376.                 '$1SplitBrowser.browserForSearch.loadURI('
  3377.             ));
  3378.         }
  3379.  
  3380.         eval('window.openUILinkIn = '+window.openUILinkIn.toSource().replace(
  3381.             '{',
  3382.             <![CDATA[$&
  3383.                 if (SplitBrowser.checkToOpenSpecialPane.apply(SplitBrowser, arguments))
  3384.                     return;
  3385.             ]]>
  3386.         ));
  3387.  
  3388.         this.overrideZoomManager();
  3389.         this.overrideCtrlTab();
  3390.         this.hackForOtherExtensions();
  3391.  
  3392.         if (this.tabbedBrowsingEnabled) {
  3393.             window.__splitbrowser__handleLinkClick = window.handleLinkClick;
  3394.             window.handleLinkClick = this.contentAreaHandleLinkClick;
  3395.         }
  3396.         if (this.getPref('splitbrowser.tabs.enabled') != this.tabbedBrowsingEnabled)
  3397.             this.setPref('splitbrowser.tabs.enabled', this.tabbedBrowsingEnabled);
  3398.  
  3399.         gBrowser.parentSubBrowser = this.mainBrowserBox;
  3400.         this.activeSubBrowser = this.mainBrowserBox;
  3401.  
  3402.         this.addPrefListener(this);
  3403.         this.onPrefChange('browser.sessionstore.enabled');
  3404.         this.onPrefChange('splitbrowser.show.syncScroll');
  3405.         this.onPrefChange('splitbrowser.show.collapseexpand');
  3406.         this.onPrefChange('splitbrowser.show.toolbar.navigation.always');
  3407.         this.onPrefChange('splitbrowser.show.menu');
  3408.         this.onPrefChange('splitbrowser.show.tab.context.split');
  3409.         this.onPrefChange('splitbrowser.show.tab.context.layout.grid');
  3410.         this.onPrefChange('splitbrowser.show.tab.context.layout.x');
  3411.         this.onPrefChange('splitbrowser.show.tab.context.layout.y');
  3412.         this.onPrefChange('splitbrowser.show.tab.context.gather');
  3413.  
  3414.  
  3415.         try {
  3416.             if (this.Prefs.prefHasUserValue('splitbrowser.show.addbuttons.hover')) {
  3417.                 this.setPref('splitbrowser.show.addbuttons.hover.type', this.getPref('splitbrowser.show.addbuttons.hover') ? 0 : 1 );
  3418.                 this.clearPref('splitbrowser.show.addbuttons.hover');
  3419.             }
  3420.         }
  3421.         catch(e) {
  3422.         }
  3423.  
  3424.         window.setTimeout(function(aSelf) {
  3425.             aSelf.delayedInit();
  3426.             aSelf.initialized = true;
  3427.  
  3428.             if (aSelf.shouldSave) {
  3429.                 window.setTimeout(function(aSelf) {
  3430.                     aSelf.load();
  3431.                 }, 100, aSelf);
  3432.             }
  3433.         }, 100, this);
  3434.     },
  3435.     
  3436.     delayedInit : function() 
  3437.     {
  3438.         if ('BrowserHandleBackspace' in window) {
  3439.             eval('window.BrowserHandleBackspace = '+window.BrowserHandleBackspace.toSource().replace(
  3440.                 /BrowserBack\(/g,
  3441.                 'SplitBrowser.activeBrowserBack('
  3442.             ));
  3443.         }
  3444.         if ('BrowserHandleShiftBackspace' in window) {
  3445.             eval('window.BrowserHandleShiftBackspace = '+window.BrowserHandleShiftBackspace.toSource().replace(
  3446.                 /BrowserForward\(/g,
  3447.                 'SplitBrowser.activeBrowserForward('
  3448.             ));
  3449.         }
  3450.  
  3451.         this.updateCommandElement('cmd_newNavigatorTab',
  3452.             'SplitBrowser.activeBrowserOpenTab();');
  3453.         this.updateCommandElement('cmd_close',
  3454.             'SplitBrowser.activeBrowserCloseTabOrWindow();');
  3455.         this.updateCommandElement('cmd_closeWindow',
  3456.             'SplitBrowser.activeBrowserTryToCloseWindow();');
  3457.         this.updateCommandElement('Browser:Back',
  3458.             'SplitBrowser.activeBrowserBack();');
  3459.         this.updateCommandElement('Browser:Forward',
  3460.             'SplitBrowser.activeBrowserForward();');
  3461.         this.updateCommandElement('Browser:Reload',
  3462.             'if (event.shiftKey) SplitBrowser.activeBrowserReloadSkipCache(); else SplitBrowser.activeBrowserReload();');
  3463.         this.updateCommandElement('Browser:ReloadSkipCache',
  3464.             'SplitBrowser.activeBrowserReloadSkipCache();');
  3465.         this.updateCommandElement('Browser:Stop',
  3466.             'SplitBrowser.activeBrowserStop();');
  3467.         this.updateCommandElement('Browser:SavePage',
  3468.             'SplitBrowser.activeBrowserSavePage();');
  3469.         this.updateCommandElement('View:PageSource',
  3470.             'SplitBrowser.activeBrowserViewPageSource();');
  3471.         this.updateCommandElement('View:PageInfo',
  3472.             'SplitBrowser.activeBrowserViewPageInfo();');
  3473.         this.updateCommandElement('Browser:AddBookmarkAs',
  3474.             'SplitBrowser.activeBrowserAddBookmarkAs();');
  3475.         this.updateCommandElement('Browser:BookmarkAllTabs',
  3476.             'SplitBrowser.activeBrowserBookmarkAllTabs();');
  3477.     },
  3478.     
  3479.     updateCommandElement : function(aId, aNewFeature) 
  3480.     {
  3481.         var node = document.getElementById(aId);
  3482.         if (node) {
  3483.             node.setAttribute('oncommand',
  3484.                 'if (SplitBrowser.isEventFromKeyboardShortcut(event)) { '+aNewFeature+'; } else { '+node.getAttribute('oncommand')+'; }');
  3485.         }
  3486.     },
  3487.   
  3488.     updateTabBrowser : function(aBrowser) 
  3489.     {
  3490.         if (aBrowser.localName != 'tabbrowser') return;
  3491.  
  3492.         var onDropFunc = '_onDrop' in aBrowser ? '_onDrop' : 'onDrop' ;
  3493.         if (onDropFunc in aBrowser) {
  3494.             eval('aBrowser.'+onDropFunc+' = '+aBrowser[onDropFunc].toSource().replace(
  3495.                 '{',
  3496.                 <![CDATA[$&
  3497.                     if (SplitBrowser.performDropOnTabBrowser(arguments, this)) {
  3498.                         // on Firefox 3.5 or later, we have to cancel this event to prevent subbrowser's handling
  3499.                         aEvent.preventDefault();
  3500.                         aEvent.stopPropagation();
  3501.                         return;
  3502.                     }
  3503.                 ]]>.toString()
  3504.             ));
  3505.         }
  3506.         if ('getSupportedFlavours' in aBrowser) {
  3507.             eval('aBrowser.getSupportedFlavours = '+aBrowser.getSupportedFlavours.toSource().replace(
  3508.                 'flavourSet.appendFlavour(',
  3509.                 'flavourSet.appendFlavour("application/x-moz-splitbrowser"); flavourSet.appendFlavour("application/x-moz-tabbrowser-tab"); $&'
  3510.             ));
  3511.         }
  3512.         if ('swapBrowsersAndCloseOther' in aBrowser) {
  3513.             eval('aBrowser.swapBrowsersAndCloseOther = '+aBrowser.swapBrowsersAndCloseOther.toSource().replace(
  3514.                 '{',
  3515.                 <![CDATA[$&
  3516.                     (function(aSelf) {
  3517.                         var d = aOtherTab.ownerDocument;
  3518.                         var w = d.defaultView;
  3519.                         var b = SplitBrowser.getTabBrowserFromChild(aOtherTab);
  3520.                         b.__splitbrowser__swappingLastMainPane = (d != document && !w.SplitBrowser.browsers.length);
  3521.                         w.setTimeout(function() { b.__splitbrowser__swappingLastMainPane = false; }, 100);
  3522.                         if (aSelf.parentSubBrowser && aSelf.parentSubBrowser.removeProgressListener) {
  3523.                             aSelf.parentSubBrowser.removeProgressListener(aOurTab);
  3524.                         }
  3525.                     })(this);
  3526.                 ]]>.toString()
  3527.             ).replace(
  3528.                 'ourBrowser.webProgress.removeProgressListener(',
  3529.                 'var __splitbrowser__reRegister = false; if (this.mTabFilters.length) { __splitbrowser__reRegister = true; $&'
  3530.             ).replace(
  3531.                 'var isBusy',
  3532.                 '} $&'
  3533.             ).replace(
  3534.                 'tabListener = this.mTabProgressListener(',
  3535.                 'if (__splitbrowser__reRegister) { $&'
  3536.             ).replace(
  3537.                 'if (aOurTab == this.selectedTab) {this.updateCurrentBrowser(',
  3538.                 '} if (this.parentSubBrowser && this.parentSubBrowser.addProgressListener) { this.parentSubBrowser.addProgressListener(aOurTab); } $&'
  3539.             ).replace(
  3540.                 'aOtherTab.ownerDocument.defaultView.getBrowser()',
  3541.                 'SplitBrowser.getTabBrowserFromChild(aOtherTab)'
  3542.             ));
  3543.         }
  3544.         if ('_beginRemoveTab' in aBrowser) {
  3545.             eval('aBrowser._beginRemoveTab = '+aBrowser._beginRemoveTab.toSource().replace(
  3546.                 /((window.)?closeWindow\([^\)]*\))/,
  3547.                 <![CDATA[(function(aSelf) {
  3548.                     var subbrowser = SplitBrowser.getSubBrowserFromChild(aSelf);
  3549.                     if (subbrowser) {
  3550.                         subbrowser.close(true);
  3551.                     }
  3552.                     else if (aSelf.__splitbrowser__swappingLastMainPane) {
  3553.                         return $1;
  3554.                     }
  3555.                     return false;
  3556.                 })(this)]]>.toString()
  3557.             ));
  3558.         }
  3559.         if ('_endRemoveTab' in aBrowser) {
  3560.             eval('aBrowser._endRemoveTab = '+aBrowser._endRemoveTab.toSource().replace(
  3561.                 /((window.)?closeWindow\([^\)]*\))/,
  3562.                 <![CDATA[(function(aSelf) {
  3563.                     var subbrowser = SplitBrowser.getSubBrowserFromChild(aSelf);
  3564.                     if (subbrowser) {
  3565.                         subbrowser.close(true);
  3566.                     }
  3567.                     else if (aSelf.__splitbrowser__swappingLastMainPane) {
  3568.                         return $1;
  3569.                     }
  3570.                     return false;
  3571.                 })(this)]]>.toString()
  3572.             ));
  3573.         }
  3574.  
  3575.         aBrowser.mTabContainer.addEventListener('select', this, false);
  3576.         aBrowser.mPanelContainer.addEventListener('load', this, true);
  3577.  
  3578.         var tabContext = document.getAnonymousElementByAttribute(aBrowser, 'anonid', 'tabContextMenu');
  3579.         tabContext.addEventListener('popupshowing', this, false);
  3580.         if (!('MultipleTabService' in window)) {
  3581.             var id = aBrowser.id || parseInt(Math.random() * 65000) ;
  3582.  
  3583.             var fragment = document.createDocumentFragment();
  3584.             fragment.appendChild(document.getElementById('splitbrowser-tab-context-item-split').cloneNode(true));
  3585.             fragment.appendChild(document.getElementById('splitbrowser-tab-context-separator-layout-grid').cloneNode(true));
  3586.             fragment.appendChild(document.getElementById('splitbrowser-tab-context-item-layout-grid').cloneNode(true));
  3587.             fragment.appendChild(document.getElementById('splitbrowser-tab-context-item-layout-x').cloneNode(true));
  3588.             fragment.appendChild(document.getElementById('splitbrowser-tab-context-item-layout-y').cloneNode(true));
  3589.             fragment.appendChild(document.getElementById('splitbrowser-tab-context-item-gather').cloneNode(true));
  3590.  
  3591.             Array.prototype.slice.call(fragment.childNodes).forEach(function(aNode) {
  3592.                 aNode.setAttribute('id', aNode.getAttribute('id').replace('template', id));
  3593.             });
  3594.  
  3595.             var separator = tabContext.firstChild;
  3596.             while (separator.localName != 'menuseparator' && separator)
  3597.             {
  3598.                 separator = separator.nextSibling;
  3599.             }
  3600.             tabContext.insertBefore(fragment, separator);
  3601.  
  3602.         }
  3603.     },
  3604.  
  3605.     destroyTabBrowser : function(aBrowser) 
  3606.     {
  3607.         if (aBrowser.localName != 'tabbrowser') return;
  3608.  
  3609.         var tabContext = document.getAnonymousElementByAttribute(aBrowser, 'anonid', 'tabContextMenu');
  3610.         tabContext.removeEventListener('popupshowing', this, false);
  3611.         aBrowser.mTabContainer.removeEventListener('select', this, false);
  3612.         aBrowser.mPanelContainer.removeEventListener('load', this, true);
  3613.     },
  3614.  
  3615.     hackForOtherExtensions : function() 
  3616.     {
  3617.     },
  3618.  
  3619.     moveAppContentContents : function(aContent, aDir) 
  3620.     {
  3621.         // this is a hack, mainly for ScrapBook
  3622.         var appcontent = document.getElementById('appcontent');
  3623.         var node = appcontent.removeChild(aContent);
  3624.         appcontent.innerContainer.insertBefore(
  3625.             aContent,
  3626.             (aDir > 0 ? null : appcontent.innerContainer.firstChild )
  3627.         );
  3628.     },
  3629.  
  3630.     newUpdateCurrentBrowser : function(aEvent, aXferData, aDragSession) 
  3631.     {
  3632.         var result = this.__splitbrowser__updateCurrentBrowser.apply(this, arguments);
  3633.  
  3634.         SplitBrowser.mainBrowserBox.focused = SplitBrowser.mainBrowserBox.focused;
  3635.  
  3636.         var node = SplitBrowser.activeSubBrowser;
  3637.         if (node != SplitBrowser.mainBrowserBox)
  3638.             node.focused = node.focused;
  3639.  
  3640.         return result;
  3641.     },
  3642.  
  3643.     contentAreaHandleLinkClick : function(aEvent, aURI, aLinkNode) 
  3644.     {
  3645.         var d = aEvent.target.ownerDocument;
  3646.         var b = SplitBrowser.getSubBrowserFromFrame(d.defaultView.top);
  3647.         if (b) {
  3648.             b = b.browser;
  3649.         }
  3650.         else {
  3651.             return this.__splitbrowser__handleLinkClick.apply(this, arguments);
  3652.         }
  3653.  
  3654.         var docURL = d.location.href;
  3655.         if (
  3656.             b.localName == 'tabbrowser' &&
  3657.             (
  3658.                 (
  3659.                     aEvent.button == 0 &&
  3660.                     aEvent.ctrlKey
  3661.                 ) ||
  3662.                 (
  3663.                     aEvent.button == 1 &&
  3664.                     SplitBrowser.getPref('browser.tabs.opentabfor.middleclick')
  3665.                 )
  3666.             )
  3667.             ) {
  3668.             var loadInBackground = SplitBrowser.getPref('browser.tabs.loadInBackground');
  3669.             if (aEvent && aEvent.shiftKey)
  3670.                 loadInBackground = !loadInBackground;
  3671.  
  3672.             if (docURL)
  3673.                 urlSecurityCheck(aURI, 'nodePrincipal' in d ? d.nodePrincipal : docURL );
  3674.  
  3675.             var originCharset = d.characterSet;
  3676.             var referrerURI = docURL ? SplitBrowser.makeURIFromSpec(docURL) : null;
  3677.             if ('loadOneTab' in b) {
  3678.                 b.loadOneTab(aURI, referrerURI, originCharset, null, loadInBackground, false);
  3679.             }
  3680.             else {
  3681.                 var tab = b.addTab(aURI, referrerURI, originCharset);
  3682.                 if (!loadInBackground) {
  3683.                     window.setTimeout(function() {
  3684.                         b.selectedTab = tab;
  3685.                     }, 0);
  3686.                     b.selectedTab = tab;
  3687.                 }
  3688.             }
  3689.             aEvent.stopPropagation();
  3690.             return true;
  3691.         }
  3692.  
  3693.         return this.__splitbrowser__handleLinkClick.apply(this, arguments);
  3694.     },
  3695.   
  3696.     destroy : function() 
  3697.     {
  3698.         this.undoCache.unregisterBroadcaster(this.undoBroadcaster);
  3699.         this.ObserverService.removeObserver(this, 'private-browsing');
  3700.  
  3701.         if (this.shouldSave && this.canSave)
  3702.             this.save();
  3703.  
  3704.         document.documentElement.removeEventListener('SubBrowserAddRequest', this, true);
  3705.         document.documentElement.removeEventListener('SubBrowserAddRequestFromContent', this, true);
  3706.         document.documentElement.removeEventListener('SubBrowserRemoveRequest', this, true);
  3707.         document.documentElement.removeEventListener('SubBrowserRemoveRequestFromContent', this, true);
  3708.         document.documentElement.removeEventListener('SubBrowserAdded', this, true);
  3709.         document.documentElement.removeEventListener('SubBrowserRemoved', this, true);
  3710.         document.documentElement.removeEventListener('SubBrowserContentCollapsed', this, true);
  3711.         document.documentElement.removeEventListener('SubBrowserContentExpanded', this, true);
  3712.         document.documentElement.removeEventListener('SubBrowserEnterContentAreaEdge', this, true);
  3713.         document.documentElement.removeEventListener('SubBrowserHoverContentAreaEdge', this, true);
  3714.         document.documentElement.removeEventListener('SubBrowserExitContentAreaEdge', this, true);
  3715.         document.documentElement.removeEventListener('SubBrowserFocusMoved', this, true);
  3716.         document.documentElement.removeEventListener('TabOpen', this, true);
  3717.         document.documentElement.removeEventListener('TabClose', this, true);
  3718.  
  3719.         document.getElementById('contentAreaContextMenu').removeEventListener('popupshowing', this, false);
  3720.  
  3721.         window.removeEventListener('resize', this, false);
  3722.         window.removeEventListener('fullscreen', this, false);
  3723.         window.removeEventListener('unload', this, false);
  3724.  
  3725.         this.destroyTabBrowser(gBrowser);
  3726.  
  3727.         this.removePrefListener(this);
  3728.  
  3729.         this._browsers.forEach(function(aBrowser) {
  3730.             SplitBrowser.destroyTabBrowser(aBrowser.browser);
  3731.             aBrowser.destroy();
  3732.             aBrowser.parentNode.removeChild(aBrowser);
  3733.         });
  3734.  
  3735.         delete gBrowser.parentSubBrowser;
  3736.     },
  3737.  
  3738.     handleEvent : function(aEvent) 
  3739.     {
  3740.         switch (aEvent.type)
  3741.         {
  3742.             case 'load':
  3743.                 if (aEvent.currentTarget == window)
  3744.                     this.init();
  3745.                 else
  3746.                     this.saveWithDelay();
  3747.                 return;
  3748.  
  3749.             case 'unload':
  3750.                 this.destroy();
  3751.                 return;
  3752.  
  3753.  
  3754.             case 'SubBrowserAddRequest':
  3755.                 window.setTimeout('SplitBrowser.hideAddButton();', 0);
  3756.                 if (aEvent.sourceTab) {
  3757.                     var oldTabs = this.tabbedBrowsingEnabled ? this.getDraggedTabs(aEvent.sourceTab) : [aEvent.sourceTab] ;
  3758.                     var oldTabBrowser = this.getTabBrowserFromChild(aEvent.sourceTab);
  3759.                     var isCloseAll = !aEvent.isCopy && (this.getTabs(oldTabBrowser).snapshotLength == oldTabs.length);
  3760.                     var subbrowser = this.addSubBrowserFromTab(oldTabs[0], aEvent.targetPosition, aEvent.targetSubBrowser, aEvent.isCopy);
  3761.                     oldTabs.splice(0, 1);
  3762.                     if (oldTabs.length) {
  3763.                         var browser = subbrowser.browser;
  3764.                         oldTabs.forEach(function(aTab) {
  3765.                             var t = browser.addTab();
  3766.                             if (aEvent.isCopy)
  3767.                                 this.cloneBrowser(aTab.linkedBrowser, t.linkedBrowser);
  3768.                             else
  3769.                                 this.swapBrowser(aTab.linkedBrowser, t.linkedBrowser);
  3770.                         }, this);
  3771.                     }
  3772.                     this.selectNewTabsAfterDrop([], oldTabBrowser);
  3773.                     if (!aEvent.isCopy) {
  3774.                         window.setTimeout(function(aSelf) {
  3775.                             aSelf.closeOldTabsAfterDrop(oldTabs, oldTabBrowser, isCloseAll);
  3776.                         }, 0, this);
  3777.                     }
  3778.                 }
  3779.                 else if (aEvent.sourceBrowser) {
  3780.                     var subbrowser = this.addSubBrowser(null, aEvent.targetSubBrowser, aEvent.targetPosition);
  3781.                     window.setTimeout(
  3782.                         this.cloneBrowser,
  3783.                         0,
  3784.                         aEvent.sourceBrowser,
  3785.                         subbrowser.browser,
  3786.                         null
  3787.                     );
  3788.                 }
  3789.                 else {
  3790.                     this.addSubBrowser(aEvent.targetURI, aEvent.targetSubBrowser, aEvent.targetPosition);
  3791.                 }
  3792.                 return;
  3793.  
  3794.             case 'SubBrowserAddRequestFromContent':
  3795.                 var cmdEvent = aEvent.sourceEvent;
  3796.                 if (!cmdEvent ||
  3797.                     cmdEvent.type.indexOf('SubBrowserAddRequest') != 0)
  3798.                     return;
  3799.  
  3800.                 window.setTimeout('SplitBrowser.hideAddButton();', 0);
  3801.                 var target = aEvent.originalTarget;
  3802.                 var win = !('nodeType' in target) ? target :
  3803.                         (target.nodeType == document.DOCUMENT_NODE) ? target.defaultView :
  3804.                         target.ownerDocument.defaultView;
  3805.                 var b = this.getSubBrowserAndBrowserFromFrame(win);
  3806.                 if (!b.browser) return;
  3807.  
  3808.                 var type = cmdEvent.type;
  3809.                 var uri = type.match(/ur[li]\s*=\s*([^\&\;]*)/i) ? decodeURIComponent(RegExp.$1) : 'about:blank' ;
  3810.                 urlSecurityCheck(uri, 'contentPrincipal' in b.browser ? b.browser.contentPrincipal : win.location.href );
  3811.  
  3812.                 if (!type.match(/pos(ition)?\s*=\s*(top|right|bottom|left|tab)/i)) return;
  3813.                 var pos = this['POSITION_'+RegExp.$2.toUpperCase()];
  3814.                 if (pos == this.POSITION_TAB) {
  3815.                     var browser = (b.browser.localName == 'tabbrowser') ? b.browser : gBrowser ;
  3816.                     var tab = browser.addTab(uri) ;
  3817.                     if (!this.getBoolPref('browser.tabs.loadInBackground'))
  3818.                         browser.selectedTab = tab;
  3819.                 }
  3820.                 else {
  3821.                     this.addSubBrowser(uri, b.subBrowser, pos);
  3822.                 }
  3823.                 return;
  3824.  
  3825.             case 'SubBrowserRemoveRequest':
  3826.                 window.setTimeout('SplitBrowser.hideAddButton();', 0);
  3827.                 this.destroyTabBrowser((aEvent.originalTarget || aEvent.target).browser);
  3828.                 window.setTimeout(function(aSelf, aSubBrowser, aPreventRestore) {
  3829.                     aSelf.removeSubBrowser(aSubBrowser, aPreventRestore);
  3830.                 }, 0, this, aEvent.originalTarget || aEvent.target, !aEvent.canRestore);
  3831.                 return;
  3832.  
  3833.             case 'SubBrowserRemoveRequestFromContent':
  3834.                 window.setTimeout('SplitBrowser.hideAddButton();', 0);
  3835.                 var target = aEvent.originalTarget;
  3836.                 var win = !('nodeType' in target) ? target :
  3837.                         (target.nodeType == document.DOCUMENT_NODE) ? target.defaultView :
  3838.                         target.ownerDocument.defaultView;
  3839.                 var b = this.getSubBrowserAndBrowserFromFrame(win);
  3840.                 if (b.subBrowser) b.subBrowser.close();
  3841.                 return;
  3842.  
  3843.  
  3844.             case 'SubBrowserRemoved':
  3845.                 if (aEvent.state)
  3846.                     this.undoCache.addEntry(aEvent.title, aEvent.icon, aEvent.state);
  3847.             case 'SubBrowserAdded':
  3848.             case 'SubBrowserContentCollapsed':
  3849.             case 'SubBrowserContentExpanded':
  3850.                 this.updateStatus();
  3851.                 this.saveWithDelay();
  3852.                 return;
  3853.  
  3854.             case 'SubBrowserEnterContentAreaEdge':
  3855.                 // ignore self-drop
  3856.                 if (aEvent.firedBy == 'dragover' &&
  3857.                     aEvent.targetSubBrowser == this.getDraggingSubBrowser())
  3858.                     return;
  3859.                 this._lastHoverX = aEvent.screenX;
  3860.                 this._lastHoverY = aEvent.screenY;
  3861.                 this.delayedShowAddButton(aEvent);
  3862.                 return;
  3863.  
  3864.             case 'SubBrowserHoverContentAreaEdge':
  3865.                 if (
  3866.                     this.addButtonIsShown &&
  3867.                     (
  3868.                         Math.abs(aEvent.screenX - this._lastHoverX) > 10 ||
  3869.                         Math.abs(aEvent.screenY - this._lastHoverY) > 10
  3870.                     )
  3871.                     ) {
  3872.                     this.stopDelayedHideAddButtonTimer();
  3873.                     this.delayedHideAddButton();
  3874.                     this._lastHoverX = aEvent.screenX;
  3875.                     this._lastHoverY = aEvent.screenY;
  3876.                 }
  3877.                 return;
  3878.  
  3879.             case 'SubBrowserExitContentAreaEdge':
  3880. //                this.hideAddButton(aEvent);
  3881.                 this.delayedHideAddButton();
  3882.                 return;
  3883.  
  3884.             case 'SubBrowserFocusMoved':
  3885.                 this.updateFindBar(aEvent);
  3886.                 this.updateMultipleTabsState();
  3887.                 this.saveWithDelay();
  3888.                 return;
  3889.  
  3890.             case 'TabOpen':
  3891.             case 'TabClose':
  3892.                 window.setTimeout('SplitBrowser.updateMultipleTabsState();', 0);
  3893.                 this.saveWithDelay();
  3894.                 return;
  3895.  
  3896.             case 'resize':
  3897.                 window.setTimeout('SplitBrowser.hideAddButton();', 0);
  3898.                 return;
  3899.  
  3900.             case 'fullscreen':
  3901.                 window.setTimeout('SplitBrowser.toggleFullScreen();', 0);
  3902.                 return;
  3903.  
  3904.             case 'popupshowing':
  3905.                 if (aEvent.target.id == 'contentAreaContextMenu') {
  3906.                     var item = document.getElementById('splitbrowser-context-item-link');
  3907.                     if (gContextMenu.onLink)
  3908.                         item.removeAttribute('hidden');
  3909.                     else
  3910.                         item.setAttribute('hidden', true);
  3911.                 }
  3912.                 else {
  3913.                     this.updateMenu(aEvent.target);
  3914.                 }
  3915.                 return;
  3916.  
  3917.             case 'select': // ontabselect
  3918.                 this.hideAddButton(aEvent, true);
  3919.                 this.saveWithDelay();
  3920.                 return;
  3921.  
  3922.  
  3923.             case 'keydown':
  3924.                 if (aEvent.shiftKey ||
  3925.                     aEvent.keyCode == aEvent.DOM_VK_SHIFT)
  3926.                     this.modifierKeyPressed = true;
  3927.                 return;
  3928.  
  3929.             case 'keyup':
  3930.                 this.modifierKeyPressed = false;
  3931.                 return;
  3932.         }
  3933.     },
  3934.     _lastHoverX : 0,
  3935.     _lastHoverY : 0,
  3936.     toggleFullScreen : function()
  3937.     {
  3938.         if (window.fullScreen)
  3939.             document.documentElement.setAttribute('splitbrowser-fullscreen', true);
  3940.         else
  3941.             document.documentElement.removeAttribute('splitbrowser-fullscreen');
  3942.     },
  3943.  
  3944.     domains : [ 
  3945.         'splitbrowser',
  3946.         'browser.sessionstore.enabled'
  3947.     ],
  3948.  
  3949.     observe : function(aSubject, aTopic, aData) 
  3950.     {
  3951.         switch (aTopic)
  3952.         {
  3953.             case 'private-browsing':
  3954.                 switch (aData)
  3955.                 {
  3956.                     case 'enter':
  3957.                         if (this.shouldSave) {
  3958.                             this.save();
  3959.                         }
  3960.                         this.removeAllSubBrowsers(true);
  3961.                         break;
  3962.                     case 'exit':
  3963.                         if (this.shouldSave) {
  3964.                             window.setTimeout(function(aSelf) {
  3965.                                 aSelf.load();
  3966.                             }, 100, this);
  3967.                         }
  3968.                         break;
  3969.                 }
  3970.                 break;
  3971.  
  3972.             case 'nsPref:changed':
  3973.                 this.onPrefChange(aData);
  3974.                 break;
  3975.         }
  3976.     },
  3977.     onPrefChange : function(aPrefstring)
  3978.     {
  3979.         switch (aPrefstring)
  3980.         {
  3981.             case 'splitbrowser.show.syncScroll':
  3982.                 if (this.getPref(aPrefstring))
  3983.                     document.documentElement.setAttribute('subbrowser-show-syncScroll-button', true);
  3984.                 else
  3985.                     document.documentElement.removeAttribute('subbrowser-show-syncScroll-button');
  3986.                 break;
  3987.  
  3988.             case 'splitbrowser.show.collapseexpand':
  3989.                 if (this.getPref(aPrefstring))
  3990.                     document.documentElement.setAttribute('subbrowser-show-togglecollapsed-button', true);
  3991.                 else
  3992.                     document.documentElement.removeAttribute('subbrowser-show-togglecollapsed-button');
  3993.                 break;
  3994.  
  3995.             case 'splitbrowser.show.toolbar.always':
  3996.                 this.splitters.forEach(
  3997.                     this.getPref(aPrefstring) ?
  3998.                         function(aSplitter) {
  3999.                             aSplitter.removeAttribute('collapse');
  4000.                         } :
  4001.                         function(aSplitter) {
  4002.                             aSplitter.setAttribute('collapse', aSplitter.getAttribute('_collapse'));
  4003.                         }
  4004.                 );
  4005.                 break;
  4006.  
  4007.             case 'splitbrowser.tabs.autoHide':
  4008.                 if (!this.tabbedBrowsingEnabled) return;
  4009.                 var visible = !this.getPref(aPrefstring);
  4010.                 this.splitters.forEach(function(aBrowser) {
  4011.                     if (this.getTabs(aBrowser.browser).snapshotLength == 1)
  4012.                         aBrowser.browser.setStripVisibilityTo(visible);
  4013.                 }, this);
  4014.                 break;
  4015.  
  4016.             case 'splitbrowser.show.toolbar.navigation.always':
  4017.                 this._browsers.forEach(
  4018.                     this.getPref(aPrefstring) ?
  4019.                         function(aBrowser) {
  4020.                             aBrowser.setAttribute('toolbar-navigation', true);
  4021.                             if (!aBrowser.contentCollapsed)
  4022.                                 aBrowser.toggleToolbar(true, true);
  4023.                         } :
  4024.                         function(aBrowser) {
  4025.                             aBrowser.removeAttribute('toolbar-navigation');
  4026.                             aBrowser.toggleToolbar(false, true);
  4027.                         }
  4028.                 );
  4029.                 break;
  4030.  
  4031.             case 'splitbrowser.show.menu':
  4032.                 var ids = 'menu,file-remove-all,view-separator,view-collapse-all,view-expand-all'.split(',');
  4033.                 if (this.getPref(aPrefstring)) {
  4034.                     document.getElementById('splitbrowser-'+ids[0]).removeAttribute('hidden');
  4035.                     ids.splice(0, 1);
  4036.                     ids.forEach(function(aID) {
  4037.                         document.getElementById('splitbrowser-'+aID).setAttribute('hidden', true);
  4038.                     });
  4039.                 }
  4040.                 else {
  4041.                     document.getElementById('splitbrowser-'+ids[0]).setAttribute('hidden', true);
  4042.                     ids.splice(0, 1);
  4043.                     ids.forEach(function(aID) {
  4044.                         document.getElementById('splitbrowser-'+aID).removeAttribute('hidden');
  4045.                     });
  4046.                 }
  4047.                 break;
  4048.  
  4049.             case 'splitbrowser.show.tab.context.split':
  4050.             case 'splitbrowser.show.tab.context.layout.grid':
  4051.             case 'splitbrowser.show.tab.context.layout.x':
  4052.             case 'splitbrowser.show.tab.context.layout.y':
  4053.             case 'splitbrowser.show.tab.context.gather':
  4054.                 var attrName = aPrefstring.replace(/\./g, '-');
  4055.                 if (this.getPref(aPrefstring))
  4056.                     document.documentElement.setAttribute(attrName, true);
  4057.                 else
  4058.                     document.documentElement.removeAttribute(attrName);
  4059.                 break;
  4060.  
  4061.             case 'browser.sessionstore.enabled':
  4062.                 this.useSessionStore = this.getPref(aPrefstring);
  4063.                 break;
  4064.         }
  4065.     },
  4066.  
  4067. /* Save/Load Prefs */ 
  4068.     
  4069.     get Prefs() 
  4070.     {
  4071.         if (!this._Prefs) {
  4072.             this._Prefs = Components.classes['@mozilla.org/preferences;1'].getService(Components.interfaces.nsIPrefBranch);
  4073.         }
  4074.         return this._Prefs;
  4075.     },
  4076.     _Prefs : null,
  4077.  
  4078.     getPref : function(aPrefstring) 
  4079.     {
  4080.         try {
  4081.             switch (this.Prefs.getPrefType(aPrefstring))
  4082.             {
  4083.                 case this.Prefs.PREF_STRING:
  4084.                     return decodeURIComponent(escape(this.Prefs.getCharPref(aPrefstring)));
  4085.                     break;
  4086.                 case this.Prefs.PREF_INT:
  4087.                     return this.Prefs.getIntPref(aPrefstring);
  4088.                     break;
  4089.                 default:
  4090.                     return this.Prefs.getBoolPref(aPrefstring);
  4091.                     break;
  4092.             }
  4093.         }
  4094.         catch(e) {
  4095.         }
  4096.  
  4097.         return null;
  4098.     },
  4099.  
  4100.     setPref : function(aPrefstring, aNewValue) 
  4101.     {
  4102.         var pref = this.Prefs ;
  4103.         var type;
  4104.         try {
  4105.             type = typeof aNewValue;
  4106.         }
  4107.         catch(e) {
  4108.             type = null;
  4109.         }
  4110.  
  4111.         switch (type)
  4112.         {
  4113.             case 'string':
  4114.                 pref.setCharPref(aPrefstring, unescape(encodeURIComponent(aNewValue)));
  4115.                 break;
  4116.             case 'number':
  4117.                 pref.setIntPref(aPrefstring, parseInt(aNewValue));
  4118.                 break;
  4119.             default:
  4120.                 pref.setBoolPref(aPrefstring, aNewValue);
  4121.                 break;
  4122.         }
  4123.         return true;
  4124.     },
  4125.  
  4126.     clearPref : function(aPrefstring) 
  4127.     {
  4128.         try {
  4129.             this.Prefs.clearUserPref(aPrefstring);
  4130.         }
  4131.         catch(e) {
  4132.         }
  4133.  
  4134.         return;
  4135.     },
  4136.  
  4137.     addPrefListener : function(aObserver) 
  4138.     {
  4139.         var domains = ('domains' in aObserver) ? aObserver.domains : [aObserver.domain || aObserver.PREFROOT] ;
  4140.         try {
  4141.             var pbi = this.Prefs.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
  4142.             for (var i = 0; i < domains.length; i++)
  4143.                 pbi.addObserver(domains[i], aObserver, false);
  4144.         }
  4145.         catch(e) {
  4146.         }
  4147.     },
  4148.  
  4149.     removePrefListener : function(aObserver) 
  4150.     {
  4151.         var domains = ('domains' in aObserver) ? aObserver.domains : [aObserver.domain || aObserver.PREFROOT] ;
  4152.         try {
  4153.             var pbi = this.Prefs.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
  4154.             for (var i = 0; i < domains.length; i++)
  4155.                 pbi.removeObserver(domains[i], aObserver, false);
  4156.         }
  4157.         catch(e) {
  4158.         }
  4159.     }
  4160.   
  4161. }; 
  4162.   
  4163. window.addEventListener('load', SplitBrowser, false); 
  4164.  
  4165.